Java Private Members Inheritance: What Subclasses Actually Get
java private members inheritance: Understand how private fields, methods, and constructors behave in Java inheritance, including access rules and design implications.
The Real Meaning of Inheritance for Private Members
In Java, the keyword private restricts access to the declaring class only. When a subclass extends a superclass, it does not inherit private members in the sense that it cannot access them directly by name. However, the private fields still exist in the memory of the subclass object, and private methods still execute when called from within the superclass. This distinction is central to understanding java private members inheritance.
Private Fields: Present but Not Accessible
Consider this example:
public class Animal { private int age; public Animal(int age) { this.age = age; } public int getAge() { return age; } } public class Dog extends Animal { public Dog(int age) { super(age); } public void describe() { // System.out.println(age); // compile error: age has private access in Animal System.out.println(getAge()); // works } }
The field age is private to Animal. A Dog object has an age field allocated, but Dog code cannot reference age directly. It must use the public getAge() method. This is not a limitation of the language; it is an encapsulation guarantee. The superclass controls how its private state is accessed.
Private Methods: Not Overridable, Not Polymorphic
Private methods are not inherited and cannot be overridden. If a subclass declares a method with the same signature as a private method in the superclass, that method is a new method, not an override. This has important consequences for polymorphism.
public class Base { private void display() { System.out.println("Base display"); } public void callDisplay() { display(); } } public class Derived extends Base { private void display() { System.out.println("Derived display"); } } // Usage Base obj = new Derived(); obj.callDisplay(); // prints "Base display"
Even though Derived has its own display(), the call inside callDisplay() is resolved to Base.display() because private methods are bound at compile time, not dynamically. This is a common source of confusion when developers expect polymorphism to apply to private methods.
Accessing Private Members Through Public or Protected Methods
The only way a subclass can interact with a superclass's private members is through methods that the superclass exposes. This is a deliberate design pattern. It allows the superclass to maintain invariants, validate inputs, or log access. For example, a setter method can enforce constraints:
public class BankAccount { private double balance; public void deposit(double amount) { if (amount > 0) { balance += amount; } } public double getBalance() { return balance; } }
A subclass cannot directly modify balance, so it cannot accidentally corrupt the account state. This is a core benefit of encapsulation.
Private Constructors and Their Effect on Inheritance
A private constructor prevents direct instantiation of the class from outside, including subclasses. If a superclass has only private constructors, it cannot be extended, because every subclass constructor must call a superclass constructor, and private constructors are not accessible to subclasses.
public class Singleton { private Singleton() {} } // class SubSingleton extends Singleton {} // compile error: Singleton() has private access in Singleton
This is often used to enforce singleton patterns or utility classes. If you need to allow subclassing, the constructor must be at least protected or public.
Common Misconceptions and Debugging Pitfalls
A frequent mistake is assuming that a subclass can access private fields by using super.field. That fails to compile. Another pitfall is declaring a method with the same name as a private method in the superclass and expecting it to be called polymorphically. As shown earlier, it will not be invoked through a superclass reference.
When debugging, remember that private members are not visible in the subclass's scope, but they are present in the object. Tools like debuggers may show them if you inspect the superclass portion of the object. Also, reflection can access private members, but that is outside normal inheritance rules.
Design Implications for Maintainability
The behavior of private members in inheritance directly affects how you design class hierarchies. Exposing private state through protected methods can weaken encapsulation, while keeping everything private forces subclasses to rely on the public API. The choice depends on whether subclasses need to customize behavior.
A common pattern is to use protected methods as hooks for subclasses, while keeping fields private. This gives subclasses the ability to modify behavior without exposing the internal state directly. For example:
public class Shape { private double area; protected void setArea(double area) { this.area = area; } public double getArea() { return area; } } public class Circle extends Shape { public Circle(double radius) { setArea(Math.PI * radius * radius); } }
Here, Circle uses the protected setter to update the private field, preserving the superclass's control over the field's type and validation.
Understanding these rules helps you avoid subtle bugs and write class hierarchies that are both flexible and safe.