Inheritance
Inheritance lets one class acquire the fields and methods of another, modeling an is-a relationship. A subclass extends a superclass to reuse and specialize its behavior. Used well, inheritance eliminates duplication; used carelessly, it creates brittle hierarchies—so apply it deliberately.
The extends Keyword
A subclass declares its parent with extends. It inherits all non-private members and may add new ones or override existing behavior.
public class Vehicle {
protected int speed;
public void accelerate() { speed += 10; }
}
public class Car extends Vehicle {
private int doors = 4;
public void honk() { System.out.println("Beep"); }
}
A Car now has speed, accelerate(), and its own honk(). Java’s universal superclass is Object; every class implicitly extends it.
Types of Inheritance
| Type | Description | Supported in Java? |
|---|---|---|
| Single | One class extends one parent | Yes |
| Multilevel | A chain: C → B → A | Yes |
| Hierarchical | Many subclasses share one parent | Yes |
| Multiple | One class extends several classes | No (classes) |
| Hybrid | A mix of the above | Only via interfaces |
Java supports multiple inheritance of type through interfaces, but not of state through classes—for the reason explained below.
Method Overriding
A subclass can replace an inherited method’s implementation by redeclaring it with the same signature. Always mark it @Override so the compiler verifies you are genuinely overriding.
public class Animal {
public String speak() { return "..."; }
}
public class Cat extends Animal {
@Override
public String speak() { return "Meow"; }
}
Animal a = new Cat();
System.out.println(a.speak());
Output:
Meow
The runtime type, not the declared type, decides which method runs—this is dynamic dispatch.
The super Keyword
super accesses the parent’s members. Use super.method() to extend (rather than fully replace) inherited behavior, and super(...) to invoke a parent constructor.
public class ElectricCar extends Car {
@Override
public void accelerate() {
super.accelerate(); // reuse parent logic
System.out.println("Silent boost");
}
}
Why No Multiple Class Inheritance: the Diamond Problem
If a class could extend two classes that both define greet(), and a fourth class inherited from both, the compiler could not decide which greet() to use—an ambiguity called the diamond problem. Java sidesteps this by forbidding multiple class inheritance. Interfaces can be implemented in multiples because, historically, they carried no state and no implementation. Since Java 8 added default methods, the language resolves any default-method clash with a strict rule: the implementing class must override the conflicting method explicitly.
Note: Prefer composition over inheritance. Holding a collaborator as a field is more flexible than locking yourself into a rigid class hierarchy.
final Classes and Methods
Marking a method final forbids overriding; marking a class final forbids extension. This protects invariants and enables certain JIT optimizations.
public final class ImmutableId { /* cannot be subclassed */ }
public class Base {
public final void criticalStep() { /* cannot be overridden */ }
}
String, Integer, and the other wrapper types are final precisely to guarantee their immutability and security.
Best Practices
- Model inheritance only for true is-a relationships; otherwise compose.
- Always annotate overrides with
@Override. - Keep base classes shallow and focused; deep hierarchies are hard to reason about.
- Use
protectedsparingly—it widens your subclass contract surface. - Mark classes
finalwhen they are not designed for extension. - Call
superwhen you mean to augment parent behavior, not silently shadow it.
Interview Questions
Q: Why doesn’t Java support multiple inheritance of classes? A: To avoid the diamond problem—ambiguity over which inherited implementation to use when two parents define the same method. Interfaces sidestep this because conflicting default methods must be resolved by an explicit override in the implementing class.
Q: What is the difference between overriding and hiding?
A: Instance methods are overridden and dispatched by runtime type. static methods and fields are hidden and resolved by the compile-time (declared) type, not the runtime object.
Q: Can a constructor be inherited?
A: No. Constructors are not inherited, but a subclass constructor must (implicitly or explicitly) call a superclass constructor via super(...).