Java course menu
Java Inheritance: extends, super, Method Overriding and Object
Learn inheritance in Java: extending a class with extends, calling the parent constructor with super(...), overriding methods with @Override, calling super.method(), protected members, the Object class, final classes and methods, and composition vs inheritance.
On this page
Inheritance lets a new class build on an existing one. The subclass (child) gets the fields and methods of the superclass (parent), and adds or changes what it needs. Use it for an "is-a" relationship: a SavingsAccount is an Account, a Car is a Vehicle.
extends and super(...)#
class Vehicle {
protected final String brand;
protected final int wheels;
Vehicle(String brand, int wheels) {
this.brand = brand;
this.wheels = wheels;
}
void describe() {
System.out.println(brand + " with " + wheels + " wheels");
}
}
class Car extends Vehicle {
private final int seats;
Car(String brand, int seats) {
super(brand, 4); // call the Vehicle constructor first
this.seats = seats;
}
void honk() {
System.out.println(brand + " says beep!"); // brand is protected: accessible here
}
int getSeats() {
return seats;
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car("Tata", 5);
car.describe(); // inherited
car.honk(); // Car's own method
System.out.println(car.getSeats() + " seats");
}
}Tata with 4 wheels
Tata says beep!
5 seatsclass Car extends VehiclemakesCara subclass ofVehicle.super(...)calls the parent's constructor and must be the first statement. If you leave it out, Java calls the parent's no-argument constructor, which fails to compile if there is none.- Java has single inheritance of classes: a class extends exactly one parent. (A class can implement many interfaces; see Interfaces.)
Overriding methods#
A subclass can replace an inherited method by declaring one with the same signature. Mark it @Override so the compiler checks you really are overriding:
class Employee {
protected final String name;
protected final double salary;
Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
double pay() {
return salary;
}
}
class Manager extends Employee {
private final double bonus;
Manager(String name, double salary, double bonus) {
super(name, salary);
this.bonus = bonus;
}
@Override
double pay() {
return super.pay() + bonus; // reuse the parent's version
}
}
public class Main {
public static void main(String[] args) {
Employee e = new Employee("Ravi", 50000);
Employee m = new Manager("Meera", 80000, 15000); // a Manager IS an Employee
System.out.println(e.name + ": " + e.pay());
System.out.println(m.name + ": " + m.pay());
}
}Ravi: 50000.0
Meera: 95000.0super.pay() calls the parent's implementation. Note that m is declared as an Employee but m.pay() still runs Manager.pay(): in Java, every instance method is chosen at run time by the object's actual class. That is polymorphism, covered in the next lesson.
Rules for overriding: same name and parameters; the return type must be the same (or a subtype); you cannot make the method less visible; and static, private and final methods cannot be overridden.
Construction order#
The parent part of an object is initialized first:
class Base {
Base() { System.out.println("Base constructor"); }
}
class Derived extends Base {
Derived() {
super(); // implicit if omitted
System.out.println("Derived constructor");
}
}
public class Main {
public static void main(String[] args) {
new Derived();
}
}Base constructor
Derived constructorprotected#
protected members are visible to subclasses (and to classes in the same package), but not to unrelated code. They are convenient, but every subclass can then depend on, and break, those details. Many developers keep fields private and expose protected methods instead.
The Object class#
Every class implicitly extends java.lang.Object, so every object has its methods, the most important being:
If you override equals, you must also override hashCode, or hash-based collections misbehave. Records generate both for you.
final classes and methods#
- A
finalmethod cannot be overridden. - A
finalclass cannot be extended.Stringis final, which is part of what makes it reliably immutable.
Composition over inheritance#
Inheritance is right only for a true "is-a" relationship. When one object has or uses another, make it a field instead (composition):
class Engine {
void start() { System.out.println("engine started"); }
}
class Car {
private final Engine engine = new Engine(); // a Car HAS an Engine
void drive() {
engine.start();
System.out.println("driving");
}
}
public class Main {
public static void main(String[] args) {
new Car().drive();
}
}engine started
driving"Prefer composition over inheritance" is classic advice from Effective Java: composition is more flexible, and subclasses do not break when the parent class changes.
Common mistakes#
Practice#
Practice exercise
Create a class Account with an owner and a balance, a deposit method and getters. Extend it with SavingsAccount, which adds an interest rate and addInterest() that deposits balance * rate. Create a savings account for "Asha" with 10000 at 4%, add interest, and print the balance.
Show solution
class Account {
private final String owner;
private double balance;
Account(String owner, double balance) {
this.owner = owner;
this.balance = balance;
}
void deposit(double amount) {
if (amount > 0) balance += amount;
}
double getBalance() { return balance; }
String getOwner() { return owner; }
}
class SavingsAccount extends Account {
private final double rate;
SavingsAccount(String owner, double balance, double rate) {
super(owner, balance);
this.rate = rate;
}
void addInterest() {
deposit(getBalance() * rate);
}
}
public class Bank {
public static void main(String[] args) {
SavingsAccount s = new SavingsAccount("Asha", 10000, 0.04);
s.addInterest();
System.out.println(s.getOwner() + ": " + s.getBalance());
}
}Asha: 10400.0SavingsAccount uses only the public interface of Account, so the parent keeps full control of the balance.
Key takeaway#
Finished this lesson?