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.