Java Inheritance
Reuse and extend class behavior in Java with the extends keyword and the rules of single inheritance.
Inheritance lets a class build on another instead of starting from scratch. The new class — the subclass — gets all of the parent's fields and methods, and adds (or replaces) only what's different. It's how Java expresses "is-a" relationships: a Cat is an Animal, an AdminUser is a User.
The keyword is extends. The mechanics are straightforward. The hard part — and the part this chapter is really about — is recognizing when inheritance is the right tool and when it isn't.
A first example
public class Animal {
String name;
void breathe() { System.out.println(name + " breathes"); }
}
public class Cat extends Animal {
void purr() { System.out.println(name + " purrs"); }
}Cat declares only purr(). It still has name and breathe(), because it inherited them:
Cat c = new Cat();
c.name = "Mittens";
c.breathe(); // Mittens breathes — inherited from Animal
c.purr(); // Mittens purrs — declared on CatAnimal is the superclass (or parent), Cat is the subclass (or child). Other languages call them base/derived class.
What gets inherited
A subclass inherits:
- All
publicandprotectedfields and methods from every ancestor. - Package-private fields and methods, if the subclass is in the same package.
- All inherited members keep their original modifiers.
A subclass does not inherit:
- Constructors. They are not members in the same sense — the subclass needs its own.
privatefields and methods. They exist in the parent (aCat's memory layout still includes any privateAnimalfields), but the subclass can't see them by name. The only way to read them is through inheritedpublic/protectedaccessor methods.
Single inheritance — one parent only
Every class extends exactly one other class. There is no multiple inheritance for classes in Java:
public class Hybrid extends Animal, Vehicle { } // ERROR — no multiple inheritanceYou can implement many interfaces (covered in interfaces) — that gives you the multiple-source flexibility most languages use multiple inheritance for, without the ambiguity headaches.
If you don't write extends at all, the class implicitly extends Object:
public class Foo { }
// is equivalent to
public class Foo extends Object { }That's why every Java object has toString(), equals(), hashCode(), and getClass() — they're all on Object. The Object class chapter goes through them.
Constructors and super
Every subclass constructor must, as its first action, call a parent constructor. If you don't write the call, Java inserts super() for you:
public class Animal {
String name;
public Animal(String name) { this.name = name; }
}
public class Cat extends Animal {
public Cat(String name) {
super(name); // call Animal(String)
}
}If Animal had no no-arg constructor and Cat didn't write super(...), the compiler would complain — there's no Animal() to insert implicitly. The super keyword chapter covers super(...) constructor chaining and super.method() calls in full.
Overriding
A subclass can replace an inherited method by declaring one with the same signature:
public class Animal {
String speak() { return "(some noise)"; }
}
public class Cat extends Animal {
@Override
String speak() { return "meow"; }
}
Cat c = new Cat();
System.out.println(c.speak()); // meow@Override is an annotation that tells the compiler "I intend this method to override an inherited one — please error out if it doesn't." Always use it. It catches typos and signature mismatches that would otherwise silently create a new method instead of overriding the old one. The method overriding chapter covers the full rules.
Upcasting and polymorphism
A subclass instance can be assigned to a variable of the parent type:
Animal a = new Cat(); // upcast — implicit
a.speak(); // calls Cat's speak() — picked at runtimeThis is the foundation of polymorphism, the next chapter. The variable a is typed Animal, but the actual object is a Cat, so the Cat version of speak runs.
When inheritance is the wrong tool
Inheritance is the most-overused mechanism in OOP. Some warning signs that you should use composition (a field of another type) instead:
- The subclass doesn't really pass an "is-a" test. A
Stackis not really aVector— yetjava.util.StackextendsVectorand is widely regarded as a design mistake. - The parent's mutable internals leak into the subclass. Changes to the parent's implementation break the subclass.
- You're inheriting to reuse a few methods, not because the types are genuinely substitutable.
The rule of thumb due to Joshua Bloch's Effective Java: prefer composition over inheritance. If B needs A's behavior but isn't really an A, give B a private field of type A and forward what it needs.
// Inheritance — fragile
public class MyList extends ArrayList<String> { ... }
// Composition — robust
public class MyList {
private final List<String> inner = new ArrayList<>();
public void add(String s) { inner.add(s); }
}The composition version is immune to surprises when ArrayList adds new methods or changes how its private fields work.
Inheritance and access
Inherited members keep the modifier they had in the parent. A subclass cannot narrow an overridden method's visibility — making a public method private in the subclass would break the Liskov substitution principle, and the compiler refuses:
public class A {
public void hello() { }
}
public class B extends A {
private void hello() { } // ERROR — cannot reduce visibility
}You can widen visibility (override a protected method as public), but rarely should.
A worked example
What's next
super came up several times here — in constructor chaining and as a way to reach an overridden parent method. The next chapter on the super keyword walks through every situation it shows up in.
Practice
Which statement about Java inheritance is true?