Skip to main content

Understanding Access Modifiers

Easy

Interview Question: "Walk me through the four access modifiers in Java. What do they actually control, what happens if you leave one off, and what's the catch with protected across packages?"

The short version​

Access modifiers are keywords that control who's allowed to see or touch a class, constructor, method, or variable. They're the actual enforcement mechanism behind encapsulation - without them, "private" would just be a suggestion.


The four visibility levels​

ModifierSame classSame packageSubclass in another packageAnywhere
private✅ Yes❌ No❌ No❌ No
default (package-private)✅ Yes✅ Yes❌ No❌ No
protected✅ Yes✅ Yes✅ Yes (with a catch)❌ No
public✅ Yes✅ Yes✅ Yes✅ Yes
privatedefaultprotectedpublicVISIBILITY SCOPE (INWARD TO OUTWARD)PrivateDeclaring class onlyDefaultSame package onlyProtectedPackage + subclasses anywherePublicAccessible everywhere
Each ring can access everything inside its boundary. private sees only its own class; public is accessible across the entire project.

The "protected across packages" catch​

This is one of the most common gotchas at the junior-to-mid level.

When a subclass lives in a different package from its parent, it can only reach an inherited protected member through its own instance - through this, basically. It cannot reach that member through a reference to the parent class, or to a sibling class, even though both are technically "the same type."

// Package: com.bank.parent
public class Account {
protected double balance;
}

// Package: com.bank.child (DIFFERENT PACKAGE)
public class SavingsAccount extends Account {
public void test() {
this.balance = 100.0; // ✅ Allowed (access via inheritance)

Account acc = new Account();
acc.balance = 100.0; // ❌ COMPILE ERROR! Not accessible through parent reference
}
}

What happens if you leave the modifier off?​

If you don't write an access modifier at all - int age = 25; - Java silently gives it default visibility.

  • Call this package-private, not just "default" - that's the term people expect in an interview. It means only classes in the exact same package can see it.
  • A top-level class can only ever be public or package-private. It can never be private or protected - those scopes just don't make sense for a file that stands on its own.

How this plays out in interfaces​

  • Fields are implicitly public static final - they're constants, not real state.
  • Methods are implicitly public abstract. Since Java 8, default and static methods are public too. Java 9 added private and private static helpers for internal use. Interfaces never allow protected methods - there's no subclass relationship for it to apply to.

How other languages handle this​

C++ lets you put an access specifier on inheritance itself, not just on members - class Derived : private Base. Private inheritance takes everything the base class exposed as public or protected and makes it private in the derived class.

Python doesn't enforce any of this. It's all convention:

  • Public: self.name
  • "Protected" by convention only: self._name - just a hint to other developers, not enforced.
  • "Private" via name mangling: self.__name - Python renames it internally to _ClassName__name, mostly to avoid accidental name clashes in subclasses, not to actually lock it down.

Clean Code Example​

Here is how access control and visibility conventions are declared across languages:

package com.example;

public class Employee {
// 1. Private: Accessible ONLY within this class
private double salary;

// 2. Package-Private (Default): Accessible within com.example
String department;

// 3. Protected: Accessible within com.example AND subclasses globally
protected String employeeId;

// 4. Public: Accessible anywhere in the entire application
public String fullName;

public Employee(double salary, String department, String id, String name) {
this.salary = salary;
this.department = department;
this.employeeId = id;
this.fullName = name;
}
}