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 class | Interface | |
|---|---|---|
| Multiple inheritance | No (single) | Yes (any number) |
| State (fields) | Yes | Constants only |
| Constructors | Yes | No |
| Best for | Shared partial implementation | Pure contract |
All sub-topics
- Classes and objects
- Constructors and
this - Inheritance and
super - Polymorphism and method overriding
- Abstraction, abstract classes, interfaces
- Encapsulation
- Records (modern data classes)
- Sealed classes
- Interfaces (default and static methods)
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.