Inheritance lets one class reuse and extend another. Polymorphism lets you treat different subclasses through a single shared type. Together they are how Java models "is-a" relationships.

Diagram: Animal parent class with Dog and Cat subclasses, each overriding makeSound()

Inheritance with extends

public class Animal {
    public void makeSound() {
        System.out.println("Some generic sound");
    }
}

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

Dog inherits everything Animal has, and @Override replaces makeSound() with its own version - this is method overriding.

Polymorphism in action

Animal a = new Dog();
a.makeSound(); // "Woof!" - Java calls Dog's version at runtime

The variable's declared type is Animal, but the actual object is a Dog - Java always calls the real object's method, not the declared type's. This is what lets you write code that works with any Animal subclass without knowing which one in advance.

Abstract classes

An abstract class cannot be instantiated directly and can declare methods with no body, forcing subclasses to implement them:

public abstract class Shape {
    public abstract double area(); // no body - subclasses must implement this
}

public class Circle extends Shape {
    private double radius;
    public Circle(double radius) { this.radius = radius; }

    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}

Interfaces

An interface is a pure contract - it says what a class must be able to do, without any implementation. A class can implement multiple interfaces, even though it can only extend one class:

public interface Payable {
    double calculatePay();
}

public class Employee implements Payable {
    @Override
    public double calculatePay() {
        return 50000.0;
    }
}