Object-Oriented Programming in Java β€” Classes, Inheritance, Polymorphism

Object-oriented programming in Java is built on four ideas: encapsulation (hide state behind methods), inheritance (specialise existing classes), polymorphism (one interface, many implementations) and abstraction (program to contracts, not implementations). Modern Java keeps these ideas but adds tools that remove most of the boilerplate they used to require.

Classes and objects

public class Circle {
    private final double radius;          // encapsulated state

    public Circle(double radius) {        // constructor
        if (radius < 0) throw new IllegalArgumentException("radius < 0");
        this.radius = radius;
    }

    public double area() {                 // behaviour
        return Math.PI * radius * radius;
    }
}

Circle c = new Circle(5);                  // create an instance
System.out.println(c.area());              // 78.539...

Records β€” modern data classes (Java 14+)

Before records, every immutable data class required a constructor, getters, equals, hashCode and toString. A record gives you all of them in one line:

public record Point(double x, double y) {}

Point p = new Point(3, 4);
System.out.println(p.x());                 // 3.0 β€” no get prefix
System.out.println(p);                     // Point[x=3.0, y=4.0]
System.out.println(p.equals(new Point(3, 4))); // true

Use a record when the class exists to carry data. Use a regular class when behaviour and state evolve over time.

Inheritance and super

public class Vehicle {
    protected final String brand;
    public Vehicle(String brand) { this.brand = brand; }
    public String describe() { return "a " + brand + " vehicle"; }
}

public class Car extends Vehicle {
    private final int doors;
    public Car(String brand, int doors) {
        super(brand);                         // call parent constructor
        this.doors = doors;
    }
    @Override
    public String describe() {
        return super.describe() + " with " + doors + " doors";
    }
}

Java supports single inheritance only β€” a class extends at most one class. Implement multiple interfaces if you need more.

Interfaces β€” contracts

public interface Shape {
    double area();                          // abstract
    default boolean isLarge() {              // default method (Java 8+)
        return area() > 100;
    }
}

public class Square implements Shape {
    private final double side;
    public Square(double side) { this.side = side; }
    @Override
    public double area() { return side * side; }
}

Polymorphism

List<Shape> shapes = List.of(new Circle(2), new Square(3));
for (Shape s : shapes) {
    System.out.println(s.area());           // calls the right area() per type
}

Sealed classes (Java 17+) β€” closed hierarchies

public sealed interface Result permits Ok, Err {}
public record Ok(String value)  implements Result {}
public record Err(String error) implements Result {}

String message = switch (result) {
    case Ok  o -> "got " + o.value();
    case Err e -> "fail " + e.error();
    // no default needed β€” compiler proves the switch is exhaustive
};

Abstraction: abstract class vs interface

Abstract classInterface
Multiple inheritanceNo (single)Yes (any number)
State (fields)YesConstants only
ConstructorsYesNo
Best forShared partial implementationPure contract

All sub-topics

Common mistakes

  • Public fields β€” bypasses encapsulation. Use private fields with getters, or a record.
  • Inheritance for code reuse alone β€” favour composition. Inheritance models is-a, not has-a.
  • Overriding without @Override β€” a typo silently creates a new method instead of overriding. Always annotate.
  • Forgetting equals/hashCode β€” broken collection lookups. Records give them for free.

Try it & related tools

The JSON to POJO tool generates POJO, record, or Lombok classes from JSON β€” pick the style that fits the use case. Run any class in the Java Online Compiler.