Command Palette

Search for a command to run...

PHASE 5Intermediate ~30 min· topic 1 of 13

Topic 5.1

Inheritance with extends

In one line

class Dog extends Animal makes Dog a kind of Animal: it gets Animal's fields and methods for free and can add its own. A class can extend only one class, and every class ultimately extends java.lang.Object.

Think of it like this

A family recipe book. Grandma's book has recipes for rice, dal and roti. When your mother starts her own book, she doesn't copy every page by hand. She starts with grandma's book and adds her own recipes at the end. Anyone who opens your mother's book can still cook grandma's dal. That is inheritance: the new book gets everything the old one had, plus its additions.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Inheritance
A way for one class to start with everything another class has, then add or change things. You write it with the keyword extends.
Superclass
The class being extended, also called the parent or base class. In Dog extends Animal, Animal is the superclass.
Subclass
The class that extends another, also called the child or derived class. In Dog extends Animal, Dog is the subclass.
is-a relationship
The test for inheritance: if 'every Dog is an Animal' is true, Dog extends Animal makes sense.
Class hierarchy
The family tree of classes formed by extends. Object is at the top, and each class has exactly one parent.
java.lang.Object
The root class of Java. Every class extends it, directly or through its parents, so every object has its methods.
protected
An access level: visible in the same package and in subclasses, even subclasses in other packages.
Single inheritance
The Java rule that a class may extend only one class.

Step by step

01The problem: copy-pasted code

Imagine a zoo app with Dog and Bird classes. Both need a name, a number of legs, an eat() method and a sleep() method. Written separately, the same fields and methods appear twice. Fix a bug in eat() and you must remember to fix it in both places. With ten animal classes, you will forget one.

Inheritance solves this by putting the shared parts in one place, a class called Animal, and letting Dog and Bird start from it.

Main.javawhole filejava
class Animal {
    String name;
    int legs = 4;

    void eat()   { System.out.println(name + " is eating"); }
    void sleep() { System.out.println(name + " is sleeping"); }
}

class Dog extends Animal {          // Dog gets name, legs, eat(), sleep()
    void bark() { System.out.println(name + " says Woof"); }
}

class Bird extends Animal {
    Bird() { legs = 2; }            // change an inherited field
    void fly() { System.out.println(name + " is flying"); }
}

02Draw the family tree

A class diagram shows the hierarchy. The arrow points from the child to the parent and reads 'extends'. Object sits at the very top even though we never wrote it.

A Dog object can do everything in its own box and everything in every box above it. A plain Animal object can't bark(): inheritance flows down the tree, never up.

Draw the family treediagram
Rendering diagram…

03What gets inherited, and what doesn't

Inherited: public, protected, and package-private fields and methods (package-private only when the subclass is in the same package). Static methods and fields are also reachable through the subclass name.

Not inherited: constructors. Dog does not get a Dog(String name) constructor just because Animal has Animal(String name). It must declare its own and pass the value up with super(name) (Topic 5.2).

Present but not visible: private fields. If Animal has private String name, every Dog object still contains a name slot, but code inside Dog that writes name gets a compile error. The field belongs to Animal's code, and Dog must go through Animal's methods, such as a getName() getter. This is encapsulation (Topic 4.5) protecting the parent from its children.

04Constructors run from the top down

When you write new Dog(), Java must build the whole object: Object's part, then Animal's part, then Dog's part. The compiler makes sure of this by putting a call to the parent constructor at the start of every constructor. If you don't write one, it inserts super(); (a call to the parent's no-argument constructor).

Inside each constructor the order is fixed: (1) call the parent constructor, (2) run this class's field initializers and instance initializer blocks in the order they appear, (3) run the rest of the constructor body.

This is why the parent's fields are always ready when the child's constructor body runs: the parent finished first.

Constructors run from the top downdiagram
Rendering diagram…

05One object, not a chain of objects

A common misunderstanding: 'new Dog() creates an Animal object and a Dog object linked together.' It doesn't. It creates one object on the heap. That single block of memory has a header (which records that its class is Dog), then slots for the fields declared in Animal, then slots for the fields declared in Dog.

The variable on the stack holds one reference to that one block. Calling d.eat() runs Animal's code on that same object; calling d.bark() runs Dog's code on the same object.

One object, not a chain of objectsdiagram
Rendering diagram…

06Only one parent class

Java forbids class C extends A, B. If A and B both had a field called id or a method start(), which one would C get? C++ allows this and has extra rules to resolve it. Java's designers chose simplicity: one parent class only.

You can still build deep trees (Object → Animal → Mammal → Dog), and a class can implement any number of interfaces (Topic 5.7). Deep trees are usually a warning sign, though; Topic 5.12 explains why.

terminal
$ javac Main.java
── expected output ──
Main.java:2: error: '{' expected
class C extends A, B { }
^
1 error

07Every class extends Object

Write class Animal { } and the compiler treats it as class Animal extends Object { }. You can see this at run time with getClass().getSuperclass(), which walks up the tree one step at a time until it reaches Object, whose superclass is null.

This is why System.out.println(anyObject) always works: println calls toString(), and every object has one, inherited from Object if nobody overrode it.

Try it yourself

  1. 1

    Add a third animal

    In the first example, add class Fish extends Animal with legs = 0 in its constructor and a swim() method. Create one in main, call eat() and swim().

    Before running, predict the exact lines it prints. Then run and compare.

  2. 2

    Predict the constructor order

    In the construction-order example, add a field String colour = log("4b. Dog second field"); *below* the Dog constructor. Predict where its line appears. (Answer: right after line 4, before line 5. Field initializers run in source order, all before the constructor body, no matter where they sit in the file.)

  3. 3

    Hide a field behind private

    Make name private in Animal and compile. Read the error, then add String getName() { return name; } to Animal and change Dog's bark() to use getName().

Code & diagrams

Dog and Bird inherit from Animal New tab

Dog never declares name, legs or eat(), yet it uses all three.

Sign in to run this example in your browser.

Expected output

Rex is eating
Rex says Woof
Rex legs: 4
Tweety is sleeping
Tweety is flying
Tweety legs: 2
The exact construction order New tab

Field initializers run after the parent constructor finishes, but before the rest of this class's constructor body.

Sign in to run this example in your browser.

Expected output

1. LivingThing constructor body
2. Animal field initializer
3. Animal constructor body
4. Dog field initializer
5. Dog constructor body
Walk up the tree to Object New tab

Object's toString() prints the class name, '@', and a hex hash code, so we only check the prefix to keep the output stable.

Sign in to run this example in your browser.

Expected output

Dog
Animal
java.lang.Object
d instanceof Animal: true
d instanceof Object: true
toString from Object: true

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Read a private parent field

Declare private String name = "Rex"; in Animal and use name directly in a Dog method.

Main.javawhole filejava
class Animal { private String name = "Rex"; }
class Dog extends Animal { String hello() { return "Hi " + name; } }
terminal
$ javac Main.java
── what you'll see ──
Main.java:2: error: name has private access in Animal
class Dog extends Animal { String hello() { return "Hi " + name; } }
^
1 error

Break #2

Parent has no no-argument constructor

Give Animal only Animal(String name) and write a Dog constructor that does not call super(...).

Main.javawhole filejava
class Animal { Animal(String name) { } }
class Dog extends Animal { Dog() { System.out.println("dog"); } }
terminal
$ javac Main.java
── what you'll see ──
Main.java:2: error: constructor Animal in class Animal cannot be applied to given types;
class Dog extends Animal { Dog() { System.out.println("dog"); } }
^
required: String
found: no arguments
reason: actual and formal argument lists differ in length
1 error

Myth vs fact

Myth

Private fields are not inherited, so a Dog object doesn't contain Animal's private fields.

Fact

The fields are there in memory and are used by Animal's methods. They're just not *accessible by name* from Dog's code. The JLS says private members are not 'inherited' in the sense of being members of the subclass, which is about naming, not storage.

Myth

new Dog() creates two objects: an Animal and a Dog.

Fact

It creates exactly one object whose class is Dog. The constructors of every ancestor run on that one object to initialise their own fields.

Myth

Inheritance is mainly for reusing code.

Fact

Reuse is a side effect. The real meaning is substitutability: a Dog can be used anywhere an Animal is expected (Topic 5.4). If you only want reuse, composition (Topic 5.12) is usually safer.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Bytecode: the compiler-inserted super() is an invokespecial Animal."<init>":()V at the start of Dog.<init>. The JVM verifier rejects any constructor that uses this (except to assign its own fields) before that call completes, which is why the object is never seen half-built from the parent's side.

  • ▸

    HotSpot lays out an object as a header (mark word plus a compressed class pointer, usually 12 bytes on 64-bit with compressed class pointers) followed by fields. Superclass fields come before subclass fields, so a superclass method can find name at the same offset in every subclass object. That fixed offset is what makes inherited field access a single load instruction.

  • ▸

    Fields are never overridden. If Dog declares its own String name, the Dog object has two name slots, and which one you read depends on the compile-time type of the reference (field hiding, shown in Topic 5.2 and 5.4). Avoid declaring a field with the same name as a parent's field.

  • ▸

    Deep hierarchies hurt. Each level couples the child to the parent's implementation details (the 'fragile base class' problem). Most well-designed Java code keeps class hierarchies two or three levels deep and uses interfaces plus composition for the rest. See the System Design course's SOLID topic (/topic/phase-1/solid) for the Liskov Substitution rule that inheritance must obey.

Remember this

  1. 1

    Inheritance lets one class reuse another class's code. You write class Dog extends Animal. Animal is the superclass (also called parent or base class) and Dog is the subclass (child or derived class). Every Dog object has all of Animal's fields and can call all of Animal's non-private methods, as if they were written inside Dog.

  2. 2

    Inheritance models an is-a relationship. A dog *is an* animal, so Dog extends Animal reads correctly. A car is *not an* engine, so Car extends Engine is wrong even though a car uses an engine (that is a *has-a* relationship, and Topic 5.12 shows the right tool for it). If the sentence 'every X is a Y' sounds false, don't use extends.

  3. 3

    What is inherited: all fields and methods that are public, protected, or package-private (no modifier, when both classes are in the same package). What is not inherited: constructors (each class writes its own) and access to private members. A private field still exists inside the subclass object and takes up memory. The subclass just can't name it directly; it must use the parent's public or protected methods.

  4. 4

    Java allows single inheritance of classes: a class can extend exactly one class. class C extends A, B does not compile. This avoids the 'diamond problem', where two parents give conflicting versions of the same field or method. (A class can implement many *interfaces*, which you'll meet in Topic 5.7.)

  5. 5

    If you don't write extends, the compiler adds extends Object for you. So every class is part of one big tree with java.lang.Object at the root, and every object you ever create has toString(), equals(), hashCode() and getClass() (Topic 4.8).

  6. 6

    When you create a subclass object, the parent part is built first. Every constructor begins by calling a parent constructor, either one you write with super(...) or an invisible super() the compiler inserts. So new Dog() runs Object's constructor, then Animal's, then Dog's: from the top of the tree down.

Explain it without notes

01

What does extends give a subclass, and what does it not give?

02

Why does Java allow only one superclass?

03

In what order do constructors and field initializers run for new C() where C extends B extends A?

04

A Dog object can't name Animal's private name field. Does the Dog object still contain that field? Explain.

Practice

01

Create Vehicle with fields brand and wheels and a method describe() that prints them. Create Car (4 wheels) and Bike (2 wheels) that extend it, each setting wheels in its constructor and adding one method of its own. Print a description of each.

02

Write a three-level hierarchy Person → Employee → Manager where each constructor prints its name, and show the order they print in.

03

Print the full superclass chain of java.util.ArrayList using getSuperclass() in a loop.

Trade-offs

  • ↔

    Inheritance removes duplication instantly, but it couples the child to the parent's internals: a change in the parent can silently change every child. Use it for true is-a relationships with a stable parent.

  • ↔

    Protected fields are convenient for subclasses but widen the parent's public surface: any subclass anywhere can now depend on them. Private fields plus protected or public methods keep the parent free to change.

  • ↔

    Deep hierarchies (four or more levels) make it hard to find where behaviour comes from. Flat hierarchies plus interfaces are easier to read and test.

Done when you can

  • Done when you can write a subclass with extends and use inherited fields and methods.

  • Done when you can list what is and isn't inherited, including constructors and private fields.

  • Done when you can predict the order of constructors and field initializers in a three-level hierarchy.

  • Done when you can explain why Java has single class inheritance and that every class extends Object.

  • Done when you can apply the is-a test to reject a bad extends.