Inheritance in Java is an object-oriented mechanism where a new class (called a subclass or child class) acquires the fields and methods of an existing class (called a superclass or parent class) using the 'extends' keyword. It establishes an 'is-a' relationship between the two classes, allowing the subclass to reuse, extend, and override the parent's functionality.
Java Inheritance
Think of inheritance like children inheriting traits from their parents. A child naturally gets certain characteristics from their parent (like eye color or last name) without needing to be taught them from scratch, but the child can also develop their own unique traits and skills beyond what their parents have. Similarly, in Java, a subclass automatically 'inherits' all the non-private fields and methods of its superclass, while still being free to add its own additional fields and methods, or even change (override) how it performs an inherited behavior.
Consider a ride-sharing app's vehicle management system. A base 'Vehicle' class defines common attributes like 'licensePlate' and 'driverName', and common methods like 'startTrip()'. Specific vehicle types like 'Car', 'Motorcycle', and 'Auto' (three-wheeler) all extend this 'Vehicle' class, automatically inheriting the shared licensePlate and driverName handling, while adding their own type-specific attributes (like 'numberOfSeats' for Car, or 'hasHelmetProvided' for Motorcycle). This lets the ride-sharing platform's core trip-management logic work generically across all vehicle types through their shared Vehicle superclass, while still allowing each vehicle type to have its own specialized attributes and fare calculation rules.
Without inheritance, common fields and methods shared across related classes would need to be manually rewritten and duplicated in every single class, leading to significant code repetition, inconsistency, and a maintenance nightmare (a bug fix or feature change would need to be applied identically in many separate places). Inheritance solves this by allowing shared logic to be defined exactly once in a common superclass, promoting code reuse (DRY principle — Don't Repeat Yourself), establishing clear hierarchical relationships between related types, and enabling powerful polymorphic behavior where code written to work with a general superclass type can seamlessly work with any of its specific subclasses.
- Single Inheritance: A subclass inherits from exactly one direct superclass, which is the only form of class inheritance Java directly supports (e.g., 'class Dog extends Animal').
- Multilevel Inheritance: A chain of inheritance where a class inherits from a subclass, which itself inherits from another superclass, forming a multi-level hierarchy (e.g., 'Puppy extends Dog extends Animal').
- Hierarchical Inheritance: Multiple distinct subclasses all inherit from the same single superclass independently (e.g., 'Dog extends Animal' and 'Cat extends Animal' both share the same parent, but are unrelated to each other).
- Multiple Inheritance (via Interfaces Only): Java does not support multiple inheritance of classes (a class cannot extend more than one class) to avoid ambiguity, but a class CAN implement multiple interfaces simultaneously, achieving a safe form of multiple inheritance of behavior contracts.
- Assuming Java Supports Multiple Class Inheritance: Writing 'class Child extends ParentA, ParentB {}' results in a compile-time syntax error, since Java does not allow a class to directly extend more than one class, specifically to avoid the 'Diamond Problem' ambiguity (where it would be unclear which parent's version of an identically-named inherited method or field should take precedence). Java only allows a class to implement multiple interfaces instead, which was specifically designed to avoid this ambiguity.
- Forgetting that super() Must Be the First Statement: If a subclass constructor needs to call its superclass's constructor using 'super(...)', this call MUST be the very first statement in the subclass's constructor. Placing any other code before it (like a print statement or field assignment) results in a compile-time error: 'call to super must be first statement in constructor'.
- Not Realizing Private Members Are Not Directly Inherited/Accessible: While a subclass technically does inherit its superclass's private fields and methods at the memory level, it cannot directly access or call them by name within its own code, since 'private' access restricts visibility strictly to the declaring class itself. Beginners are often confused when a subclass cannot directly reference a superclass's private field, even though an object of the subclass technically contains that field in memory — access must go through inherited public/protected getter methods instead.
- Overriding a Method with a More Restrictive Access Modifier: Attempting to override a superclass's 'public' method with a 'protected' or 'private' version in the subclass causes a compile-time error: 'attempting to assign weaker access privileges'. An overriding method's access modifier must be the same as, or more permissive (more public) than, the method it is overriding in the superclass, never more restrictive.
- Use 'extends' Only for Genuine IS-A Relationships: Before using inheritance, confirm that the relationship genuinely fits an 'is-a' pattern (a Manager IS AN Employee, a Car IS A Vehicle). If the relationship is more like 'HAS-A' (a Car has an Engine), use composition (declaring a field of that type) instead of inheritance, to avoid creating an inappropriate, rigid class hierarchy.
- Always Call super() Explicitly When the Parent Has No No-Argument Constructor: If a superclass does not define a no-argument constructor (only a parameterized one), every subclass constructor MUST explicitly call 'super(...)' with matching appropriate arguments as its first statement, since Java would otherwise implicitly try to call a nonexistent no-argument superclass constructor, causing a compile-time error.
- Favor Interfaces for Achieving Multiple Inheritance-Like Behavior: Since Java doesn't support multiple class inheritance, use interfaces when a class genuinely needs to fulfill multiple, potentially unrelated behavioral contracts (e.g., a class that needs to be both 'Comparable' and 'Serializable'), since a class can implement as many interfaces as needed simultaneously.
- Keep Inheritance Hierarchies Shallow: Avoid creating overly deep inheritance chains (e.g., more than 3-4 levels like A extends B extends C extends D extends E), since deep hierarchies become increasingly difficult to understand, debug, and modify safely, as changes to a base class can have unpredictable ripple effects across many descendant levels.
Inheritance allows a Java subclass to reuse and extend the fields and methods of a superclass using the 'extends' keyword, establishing an 'is-a' relationship and supporting single, multilevel, and hierarchical inheritance patterns. The 'super' keyword enables a subclass to explicitly invoke its parent's constructor or reuse its parent's method implementation within an override. While Java deliberately avoids multiple class inheritance to prevent ambiguity, it achieves similar flexibility safely through interfaces, and understanding constructor chaining rules and access modifier restrictions during overriding is essential for building correct, maintainable class hierarchies.