EMZETT.
Login

Inheritance

In short: A class (subclass) takes over fields and methods of another class (superclass) with the extends keyword — saves repetition and represents “is-a” relationships.

In more detail: Java only allows single inheritance: a class can inherit from exactly one superclass with extends (unlike interfaces, of which a class can implement several at the same time). The subclass can re-implement inherited methods with the same signature (“overriding”) — the basis for polymorphism.

class Animal { void makeSound() { System.out.println("..."); } }
class Dog extends Animal { void makeSound() { System.out.println("Woof"); } }

In Depth

class Animal {
    protected String name;
 
    Animal(String name) { this.name = name; }
 
    void makeSound() { System.out.println(name + " makes a sound"); }
    void eat() { System.out.println(name + " is eating"); } // not overridden
}
 
class Dog extends Animal {
    Dog(String name) {
        super(name); // calls Animal's constructor - mandatory as the first line
    }
 
    @Override
    void makeSound() { System.out.println(name + " barks: Woof!"); }
}
 
Animal a = new Dog("Rex"); // polymorphism: variable of type Animal, object of type Dog
a.makeSound(); // "Rex barks: Woof!" - the OVERRIDDEN version runs, not Animal's
a.eat();       // "Rex is eating" - inherited, unchanged

Java deliberately only allows single inheritance (extends only ONE superclass) to avoid the so-called “diamond problem”: if a class inherited from two superclasses that both implement the same method differently, it would be unclear which version applies. Interfaces sidestep this problem because they (traditionally) don’t bring their own implementation — a class can therefore implement any number of interfaces, but inherit from only one class. protected (instead of private) for superclass fields allows subclasses direct access, while access from outside the inheritance hierarchy remains forbidden — a middle ground between encapsulation and practical reusability in subclasses.

final prevents further inheritance

A class (or a single method) can be specifically protected from further inheritance or overriding with final:

final class Constant {} // can NO LONGER be extended by ANY class
 
class Base {
    final void criticalMethod() {} // CANNOT be overridden in subclasses
}

Well-known examples from the standard library: String itself is final — that’s exactly why nobody can build their own MyString extends String class, which protects the class’s security and consistency guarantees (e.g. that a String object really always stays immutable).

Common pitfall: field shadowing instead of overriding

Unlike methods, FIELDS aren’t “overridden” during inheritance, only shadowed — which field is actually used depends on the declared type of the reference, not the actual object type:

class A { String name = "A"; }
class B extends A { String name = "B"; } // shadows A.name, does NOT override it
 
A a = new B();
System.out.println(a.name); // "A" - the declared type (A) decides for fields!

This differs fundamentally from methods, where the ACTUAL object type (dynamic binding) decides which version runs — a common point of confusion for beginners who mentally treat fields and methods the same way.

See also: Polymorphism, super Keyword, Interface, OOP