Back to Blog
Java

Java Protected: Access Rules and Practical Use

java protected: Understand the protected access modifier in Java: its exact rules, common pitfalls, and when to use it in inheritance design.

javaaccess-modifiersinheritanceencapsulationoop
Diagram showing Java protected access across packages and subclasses

The protected keyword in Java controls access to class members in a way that often confuses developers. Unlike private or public, protected sits between them, allowing access within the same package and by subclasses, even when those subclasses live in a different package. This article explains the precise rules of java protected, demonstrates common usage patterns, and highlights the mistakes that arise when its behavior is misunderstood.

The Exact Rules of the protected Modifier

A member declared as protected is accessible from three places:

  • Within the same class.
  • Within the same package, regardless of inheritance.
  • Within any subclass, even if the subclass is in a different package.

The third rule has a subtle constraint: a subclass can access a protected member only through a reference whose type is the subclass itself (or a further subclass). It cannot access the protected member through a reference of the superclass type, even if the object is an instance of the subclass.

package com.example.base; public class Base { protected int value = 10; }
package com.example.derived; import com.example.base.Base; public class Child extends Base { public void access() { value = 20; // OK: accessing via this reference } public void accessOther(Base other) { // other.value = 30; // Compile error: cannot access protected member via superclass reference } }

The commented line fails because other is declared as Base. Even though the actual object might be a Child, the compiler only sees the declared type. This rule prevents a subclass from accessing protected members of arbitrary superclass instances, preserving encapsulation.

How protected Differs from Other Access Levels

Java provides four access levels. The following table summarizes what each allows:

Access ModifierSame ClassSame PackageSubclass (different package)Anywhere
privateYesNoNoNo
package-privateYesYesNoNo
protectedYesYesYes (with type restriction)No
publicYesYesYesYes

Package-private is the default when no modifier is written. It is often overlooked, but it provides a meaningful middle ground when you want to share members within a package without exposing them to subclasses in other packages.

Using protected for Template Method Patterns

The protected modifier is a natural fit for the Template Method pattern. A base class defines the skeleton of an algorithm and lets subclasses override specific steps without exposing those steps to external callers.

public abstract class DataParser { public final void parse(String source) { open(source); read(); close(); } protected abstract void open(String source); protected abstract void read(); protected void close() { // Default implementation } }

Here, parse is public because it is the entry point. The open and read methods are protected because they are extension points for subclasses. External code cannot call them directly, but subclasses can override them. This keeps the public API small while allowing controlled customization.

Common Mistakes with protected

One frequent error is assuming that protected grants access to any subclass instance, regardless of the reference type. As shown earlier, the compiler enforces the declared type. Another mistake is confusing protected with package-private when the subclass is in the same package. In that case, both work, but the distinction matters when the subclass moves to another package.

Consider this scenario:

package com.example.a; public class A { protected void method() { } }
package com.example.b; import com.example.a.A; public class B extends A { public void call() { method(); // OK } }

If B were in the same package, method() would also be accessible without inheritance. But the moment B moves to a different package, the inheritance relationship becomes the only reason access is allowed. This subtle shift is a common source of confusion when refactoring packages.

When to Choose protected Over Other Access Levels

Choosing protected is a design decision. Use it when you want to expose a member to subclasses but hide it from the rest of the world. This is typical for:

  • Hook methods in a template method pattern.
  • Fields that subclasses need to initialize or modify during construction.
  • Internal helper methods that subclasses may override to change behavior.

Avoid protected for members that are only used internally by the class itself; private is safer because it limits the blast radius of changes. Also avoid protected for members that are part of the public contract; use public instead. The key is to expose the minimum surface area needed for extension.

protected and Inheritance Across Packages

The type restriction on protected access becomes especially important when working across packages. A subclass can access a protected member through this or through a reference of its own type, but not through a superclass reference. This rule applies to both fields and methods.

package com.example.base; public class Base { protected void log() { } }
package com.example.child; import com.example.base.Base; public class Child extends Base { public void demo() { log(); // OK Child c = new Child(); c.log(); // OK Base b = new Child(); // b.log(); // Compile error } }

The restriction prevents a subclass from calling protected methods on arbitrary base-class references. This is not a limitation but a safety mechanism. It ensures that protected members are only accessed through the subclass that inherits them, preserving the integrity of the base class's internal state.

Design Considerations and Maintainability

Using protected affects the maintainability of your code in several ways. First, it expands the API surface beyond the public methods, because subclasses become part of the contract. Any change to a protected member can break existing subclasses, even if they are in different packages. This is a form of coupling that must be managed carefully.

Second, protected members are visible to all classes in the same package. If your package contains many unrelated classes, they can all access these members, which might be unintended. In such cases, consider using package-private or private and provide protected accessors only if necessary.

Third, testing becomes slightly more involved. Protected members can be accessed from test classes if they are in the same package, but not from subclasses in other packages. You may need to create a subclass in the test package to exercise protected behavior. This is a common pattern but adds a layer of indirection.

Finally, when evolving a class, think about whether a member truly needs to be protected or whether it can be private with a protected final method for extension. Reducing the number of protected members lowers the risk of accidental coupling and makes the class easier to refactor.

java protected: Practical Usage and Code Examples | RYUSLOG DEV