Java tutorials  /  Java Inheritance
Chapter 11 · Java

Java Inheritance

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.

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.
class SuperClass { // fields and methods } class SubClass extends SuperClass { // additional fields and methods // can override SuperClass methods // can use super.methodName() or super(constructorArgs) }
A beginner wants to model a company's staff hierarchy where a general 'Employee' class defines shared attributes like name and base salary, and a 'Manager' subclass extends it to add a 'teamSize' field and a bonus calculation, while still reusing the base salary logic from Employee using the 'super' keyword.
Basic Inheritance with the super Keyword
This example demonstrates a Manager subclass extending an Employee superclass, using 'super()' to invoke the parent's constructor and 'super.methodName()' to reuse and extend the parent's calculation logic.
Java
class Employee { String name; double baseSalary; public Employee(String name, double baseSalary) { this.name = name; this.baseSalary = baseSalary; } public double calculateSalary() { return baseSalary; } } class Manager extends Employee { int teamSize; public Manager(String name, double baseSalary, int teamSize) { super(name, baseSalary); this.teamSize = teamSize; } @Override public double calculateSalary() { double bonus = teamSize * 500; return super.calculateSalary() + bonus; } } public class InheritanceDemo { public static void main(String[] args) { Manager manager = new Manager("Priya", 60000.0, 4); System.out.println(manager.name + "'s total salary: " + manager.calculateSalary()); } }
Priya's total salary: 62000.0
'class Manager extends Employee' establishes inheritance, giving Manager access to Employee's 'name' and 'baseSalary' fields. The Manager constructor's first line, 'super(name, baseSalary);', explicitly calls the Employee superclass's constructor to properly initialize the inherited fields. Inside the overridden 'calculateSalary()' method, 'super.calculateSalary()' specifically calls the ORIGINAL Employee version of this method (returning just baseSalary), to which the manager's own bonus is added, demonstrating how a subclass can reuse and extend, rather than completely replace, a parent's logic.
Hierarchical Inheritance: Multiple Subclasses Sharing One Superclass
This example shows hierarchical inheritance, where both a 'Car' and a 'Motorcycle' class independently extend the same 'Vehicle' superclass, each adding their own unique field while reusing the shared 'displayInfo()' method.
Java
class Vehicle { String licensePlate; public Vehicle(String licensePlate) { this.licensePlate = licensePlate; } public void displayInfo() { System.out.println("License Plate: " + licensePlate); } } class Car extends Vehicle { int numberOfSeats; public Car(String licensePlate, int numberOfSeats) { super(licensePlate); this.numberOfSeats = numberOfSeats; } } class Motorcycle extends Vehicle { boolean hasHelmetProvided; public Motorcycle(String licensePlate, boolean hasHelmetProvided) { super(licensePlate); this.hasHelmetProvided = hasHelmetProvided; } } public class HierarchicalDemo { public static void main(String[] args) { Car car = new Car("KA-01-1234", 5); Motorcycle bike = new Motorcycle("KA-02-5678", true); car.displayInfo(); bike.displayInfo(); } }
License Plate: KA-01-1234 License Plate: KA-02-5678
Both 'Car' and 'Motorcycle' independently extend the same 'Vehicle' superclass, meaning each automatically inherits the 'licensePlate' field and 'displayInfo()' method without needing to redefine them. Both subclasses call 'super(licensePlate)' in their own constructors to properly initialize the inherited field, and both can call the inherited 'displayInfo()' method directly, even though it's defined only once in Vehicle — this is hierarchical inheritance, reusing one common superclass across multiple independent subclasses.
  • 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.
Why doesn't Java support multiple inheritance of classes, and how does it achieve similar functionality safely?
Java deliberately does not allow a class to extend more than one class specifically to avoid the 'Diamond Problem' — a scenario of ambiguity where, if two parent classes both defined a method or field with the exact same name and signature, the compiler would have no unambiguous way to determine which parent's version the subclass should inherit or use. Java achieves safe, multiple-inheritance-like functionality instead through interfaces, which a class can implement in any number simultaneously; since interfaces (prior to default methods) contained no implementation at all, there was no ambiguity to resolve, and even with Java 8's default methods, the language enforces explicit resolution rules if a genuine conflict arises between multiple implemented interfaces' default methods.
What is the difference between method overriding and method hiding in the context of inheritance?
Method overriding applies specifically to instance methods, where a subclass provides its own implementation of a method with the exact same signature as one in its superclass; which version executes is determined at RUNTIME based on the object's actual type, enabling polymorphism. Method hiding, in contrast, applies to STATIC methods — if a subclass defines a static method with the same signature as a static method in its superclass, it doesn't override it in the polymorphic sense; instead, it 'hides' it, and which version gets called is determined at COMPILE TIME based on the reference variable's declared type, not the actual object's runtime type, since static methods belong to the class itself, not to any particular object instance.
If a superclass constructor requires parameters and has no no-argument constructor, what must every subclass do, and what happens if this rule is violated?
Every subclass's constructor must explicitly call the superclass's parameterized constructor using 'super(appropriate arguments)' as the very first statement within its own constructor body. This is required because, by default, if a subclass constructor does not explicitly call 'super(...)', Java automatically inserts an implicit, no-argument 'super();' call as the first statement. If the superclass doesn't actually have a no-argument constructor available (because it only defined a parameterized one), this implicit call fails, resulting in a compile-time error: 'constructor Employee in class Employee cannot be applied to given types; reason: actual and formal argument lists differ in length', forcing the developer to add an explicit, matching 'super(...)' call.
Create a superclass 'Shape' with a protected field 'color' and a constructor, and a subclass 'Square' that extends it, adding a 'sideLength' field, and prints both the color and calculated area.
class Shape { protected String color; public Shape(String color) { this.color = color; } } class Square extends Shape { double sideLength; public Square(String color, double sideLength) { super(color); this.sideLength = sideLength; } public double calculateArea() { return sideLength * sideLength; } } public class ShapeInheritanceDemo { public static void main(String[] args) { Square square = new Square("Red", 4.0); System.out.println("Color: " + square.color); System.out.println("Area: " + square.calculateArea()); } } // Output: // Color: Red // Area: 16.0
What will happen when you try to compile the following code, and why? class Parent { public Parent(String name) { System.out.println("Parent constructor: " + name); } } class Child extends Parent { public Child() { System.out.println("Child constructor"); } }
This code will fail to compile with an error similar to: 'constructor Parent in class Parent cannot be applied to given types; required: String; found: no arguments'. This happens because the 'Child' constructor does not explicitly call 'super(...)', so Java automatically attempts to insert an implicit, no-argument 'super();' call as the first statement. However, 'Parent' only defines a constructor that requires a String parameter and has no true no-argument constructor available, so this implicit call fails. The fix requires explicitly calling the parameterized constructor: 'public Child() { super("DefaultName"); System.out.println("Child constructor"); }'.
Demonstrate multilevel inheritance by creating a class chain: 'Animal' (with a 'breathe()' method), 'Mammal extends Animal' (with a 'walk()' method), and 'Dog extends Mammal' (with a 'bark()' method). Call all three methods on a Dog object.
class Animal { public void breathe() { System.out.println("Breathing..."); } } class Mammal extends Animal { public void walk() { System.out.println("Walking..."); } } class Dog extends Mammal { public void bark() { System.out.println("Barking..."); } } public class MultilevelDemo { public static void main(String[] args) { Dog myDog = new Dog(); myDog.breathe(); myDog.walk(); myDog.bark(); } } // Output: // Breathing... // Walking... // Barking... // This works because Dog inherits transitively through the chain: Dog extends Mammal, and Mammal extends Animal, so Dog automatically gains access to methods defined at every level of this multilevel hierarchy, not just its immediate parent.

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.

© 2026 CompileX. Maintained by Aditya Kumar Sharma.