Access Control Rules
Access control rules in Java define who can access what—classes, constructors, variables, and methods—across classes, packages, and inheritance hierarchies. They are central to encapsulation, security, and clean API design.
The Four Access Levels (Least → Most Accessible)
private < default (package-private) < protected < public
Where Access Modifiers Can Be Applied
| Target | Allowed Modifiers |
|---|---|
| Top-level class | public, default |
| Member class | all four |
| Methods | all four |
| Variables (fields) | all four |
| Constructors | all four |
Core Visibility Matrix (Interview Favorite)
| Modifier | Same Class | Same Package | Subclass (diff pkg) | Everywhere |
|---|---|---|---|---|
| private | ✔ | ✖ | ✖ | ✖ |
| default | ✔ | ✔ | ✖ | ✖ |
| protected | ✔ | ✔ | ✔* | ✖ |
| public | ✔ | ✔ | ✔ | ✔ |
* Protected nuance: accessible in a subclass only via inheritance (not via a parent reference from outside the package).
Detailed Rules by Modifier
1) private
- Scope: within the declaring class only
- Not inherited
- Best for fields and internal helpers
class A {
private int x;
}
2) default (package-private)
- Scope: same package
- Not accessible from other packages (even subclasses)
class A { int y; } // default
3) protected
- Scope: same package OR subclasses in other packages
- Subclass rule: access through this/inheritance, not through a parent instance
package p1;
public class Parent { protected int v; }
package p2;
public class Child extends Parent {
void ok() { System.out.println(v); } // ✅
void bad(Parent p) {
// System.out.println(p.v); // ❌ outside package via parent ref
}
}
4) public
- Scope: everywhere
- Use for APIs and entry points
public class Service {
public void run() {}
}
Class-Level Access Rules
- Top-level classes: only public or default
- One public class per file, filename must match
public class A {} // A.java
class Helper {} // default
Constructor Access Control
- Controls who can create objects
- Common patterns:
- private constructor → Singleton / Utility
- protected → framework-controlled instantiation
class Util {
private Util() {} // no instances
}
Access Control with Inheritance (Key Points)
- private members: not inherited
- Overriding rules:
- Cannot reduce visibility
- Can increase visibility
class P { protected void f() {} }
class C extends P { public void f() {} } // ✅
Packages as Visibility Boundaries
- default and protected rely on package boundaries
- Sub-packages are independent; no implicit access
com.app
├─ service
└─ util // util ≠ service
Best Practices (Production-Grade)
- Use the most restrictive modifier possible
- Fields → private
- Methods → expose minimal public API
- Prefer specific protected over public in frameworks
- Avoid default for public libraries (ambiguous API surface)
Common Pitfalls
- Assuming protected == package-only
- Accessing protected via parent reference across packages
- Overexposing fields as public
- Expecting sub-packages to inherit access
Interview-Ready Answers
Short:
Access control rules define the visibility of classes and members using private, default, protected, and public.
Detailed:
Java access control governs where classes and members can be accessed—within a class, package, subclass, or globally. private restricts to the class, default to the package, protected adds subclass access across packages (via inheritance), and public allows universal access. Proper use enforces encapsulation and secure APIs.
Key Takeaway
Access control is the gatekeeper of encapsulation. Design with the smallest visible surface area to achieve safe, maintainable, and scalable Java code.
Access Control Examples (Quick Reference)
1. public Access – Everywhere Accessible
package p1;
public class A {
public int x = 10;
}
package p2;
import p1.A;
class Demo {
public static void main(String[] args) {
A a = new A();
System.out.println(a.x);
}
}
Explanation
- public → accessible from any package
- Output: 10
2. private Access – Same Class Only
class A {
private int x = 10;
void show() {
System.out.println(x);
}
}
class Demo {
public static void main(String[] args) {
A a = new A();
// System.out.println(a.x); // ❌ not accessible
a.show();
}
}
Explanation
- private → class-level access only
- Output: 10
3. Default (Package-Private) Access – Same Package
package p1;
class A {
int x = 10; // default
}
package p1;
class Demo {
public static void main(String[] args) {
A a = new A();
System.out.println(a.x);
}
}
Explanation
- Default → accessible only within same package
- Output: 10
4. Default Access – Different Package (Not Allowed)
package p1;
class A {
int x = 10;
}
package p2;
// import p1.A;
class Demo {
public static void main(String[] args) {
// A a = new A(); // ❌ not accessible
}
}
Explanation
- Default members cannot cross package boundary
5. protected Access – Same Package (No Inheritance Needed)
package p1;
public class A {
protected int x = 10;
}
package p1;
class Demo {
public static void main(String[] args) {
A a = new A();
System.out.println(a.x);
}
}
Explanation
- protected → accessible within same package
- Output: 10
6. protected Access – Different Package via Inheritance
package p1;
public class A {
protected int x = 10;
}
package p2;
import p1.A;
class B extends A {
public static void main(String[] args) {
B b = new B();
System.out.println(b.x);
}
}
Explanation
- protected works across packages only through inheritance
- Output: 10
7. protected Access – Different Package Without Inheritance (Not Allowed)
package p2;
import p1.A;
class Demo {
public static void main(String[] args) {
A a = new A();
// System.out.println(a.x); // ❌ not accessible
}
}
Explanation
- protected ≠ public
- Needs subclass reference
8. Access Control with Methods
class A {
private void m1() {}
void m2() {}
protected void m3() {}
public void m4() {}
}
Explanation
- Same rules apply to methods as variables
9. Overriding – Cannot Reduce Visibility
class A {
protected void show() {}
}
class B extends A {
// void show() {} // ❌ cannot reduce visibility
}
Explanation
- Visibility must be same or wider
10. Overriding – Increasing Visibility Allowed
class A {
protected void show() {
System.out.println("A");
}
}
class B extends A {
public void show() {
System.out.println("B");
}
public static void main(String[] args) {
new B().show();
}
}
Explanation
- protected → public allowed
- Output: B
11. private Methods Are Not Overridden
class A {
private void show() {
System.out.println("A");
}
}
class B extends A {
void show() {
System.out.println("B");
}
public static void main(String[] args) {
new B().show();
}
}
Explanation
- private methods are class-specific
- Output: B
12. Access Control with Constructors
class A {
private A() {}
}
class Demo {
public static void main(String[] args) {
// new A(); // ❌ constructor not accessible
}
}
Explanation
- private constructors prevent object creation (Singleton pattern)
13. Access Modifiers with Abstract Methods
abstract class A {
protected abstract void show();
}
class B extends A {
public void show() {
System.out.println("Implemented");
}
public static void main(String[] args) {
new B().show();
}
}
Explanation
- Abstract method visibility can be widened
- Output: Implemented
14. Access Control with Interfaces
interface A {
int x = 10;
void show();
}
Explanation
- Interface members are public by default
- public static final variables
- public abstract methods
15. Access Modifiers and Packages (Summary Code)
package p1;
public class A {
public int a = 1;
protected int b = 2;
int c = 3;
private int d = 4;
}
package p2;
import p1.A;
class B extends A {
void test() {
System.out.println(a); // ✔ public
System.out.println(b); // ✔ protected
// System.out.println(c); // ❌ default
// System.out.println(d); // ❌ private
}
}
16. Access Control with Static Members
class A {
protected static int x = 10;
}
class B extends A {
public static void main(String[] args) {
System.out.println(x);
}
}
Explanation
- Access rules apply same to static
17. Access Control with Nested Classes
class Outer {
private int x = 10;
class Inner {
void show() {
System.out.println(x);
}
}
public static void main(String[] args) {
Outer.Inner i = new Outer().new Inner();
i.show();
}
}
Explanation
- Inner class can access private members
- Output: 10
18. Access Control and this vs super
class A {
protected int x = 10;
}
class B extends A {
int x = 20;
void show() {
System.out.println(this.x);
System.out.println(super.x);
}
public static void main(String[] args) {
new B().show();
}
}
Output
20
10
19. Common Interview Trap
class A {
protected void show() {}
}
class B extends A {
// private void show() {} // ❌ compile-time error
}
Explanation
- Cannot reduce visibility while overriding
20. Interview Summary – Access Control Rules
class Demo {
public int a;
protected int b;
int c;
private int d;
}
Key Rules
- public → everywhere
- protected → same package + subclass
- default → same package only
- private → same class only
- Overriding → visibility cannot be reduced