← Back to Home

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