thecodex.expert · The Codex Family of Knowledge
Java

OOP in Java in Java

Class-based, single inheritance, multiple interfaces — and modern Java adds records and sealed classes that make OOP concise.

Java 21 LTSOpenJDKLast verified:
Canonical Definition

Java OOP is class-based with single-class inheritance and multiple interface implementation — every class is an Object, abstract classes provide partial implementations, interfaces define behaviour contracts, and modern Java adds records (immutable data classes) and sealed classes (restricted hierarchies).

One sentence

Java OOP is class-based and strictly single-inheritance — every class extends exactly one parent (Object if unspecified), but can implement multiple interfaces.

Classes and objects

JavaBankAccount.java
public class BankAccount {
    // Instance fields
    private final String owner;   // final = set once in constructor
    private double balance;

    // Constructor
    public BankAccount(String owner, double initialBalance) {
        if (initialBalance < 0) {
            throw new IllegalArgumentException("Balance cannot be negative");
        }
        this.owner   = owner;
        this.balance = initialBalance;
    }

    // Methods
    public void deposit(double amount) {
        if (amount <= 0) throw new IllegalArgumentException("Amount must be positive");
        balance += amount;
    }

    public void withdraw(double amount) {
        if (amount > balance) throw new IllegalStateException("Insufficient funds");
        balance -= amount;
    }

    // Getters — no setters for immutable fields
    public String getOwner()   { return owner; }
    public double getBalance() { return balance; }

    @Override
    public String toString() {
        return String.format("BankAccount{owner='%s', balance=%.2f}", owner, balance);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof BankAccount that)) return false;
        return owner.equals(that.owner);   // same owner = same account
    }

    @Override
    public int hashCode() {
        return owner.hashCode();
    }
}

Inheritance and polymorphism

Javainheritance.java
// Abstract class — cannot be instantiated, may have abstract methods
abstract class Shape {
    abstract double area();
    abstract double perimeter();

    // Concrete method — shared by all shapes
    void describe() {
        System.out.printf("Area: %.2f, Perimeter: %.2f%n", area(), perimeter());
    }
}

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

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

class Rectangle extends Shape {
    private final double w, h;
    Rectangle(double w, double h) { this.w = w; this.h = h; }

    @Override double area()      { return w * h; }
    @Override double perimeter() { return 2 * (w + h); }
}

// Polymorphism — same variable type, different runtime behaviour
Shape[] shapes = { new Circle(5), new Rectangle(4, 3) };
for (Shape s : shapes) {
    s.describe();   // calls Circle.area() or Rectangle.area() at runtime
}

Interfaces

Javainterfaces.java
// Interface — a contract (all methods public by default)
interface Printable {
    void print();

    // Default method (Java 8+) — optional implementation
    default void printWithBorder() {
        System.out.println("---");
        print();
        System.out.println("---");
    }

    // Static method in interface (Java 8+)
    static Printable of(String text) {
        return () -> System.out.println(text);   // lambda
    }
}

interface Saveable {
    void save(String path);
}

// A class can implement MULTIPLE interfaces (but extend only ONE class)
class Document implements Printable, Saveable {
    private String content;

    Document(String content) { this.content = content; }

    @Override public void print() { System.out.println(content); }
    @Override public void save(String path) { /* write to file */ }
}

// Functional interface — exactly one abstract method, usable as lambda
@FunctionalInterface
interface Transformer<T, R> {
    R transform(T input);
}

Transformer<String, Integer> lengthOf = String::length;
System.out.println(lengthOf.transform("Hello"));   // 5

Records, sealed classes, and modern Java patterns

Javamodern_oop.java
// Record — immutable data class (Java 16+)
// Generates constructor, getters, equals, hashCode, toString automatically
record Person(String name, int age) {
    // Compact canonical constructor for validation
    Person {
        Objects.requireNonNull(name, "name must not be null");
        if (age < 0) throw new IllegalArgumentException("age must be non-negative");
    }
}

// Sealed class — restrict the class hierarchy (Java 17+)
sealed interface Result<T> permits Success, Failure {
    record Success<T>(T value) implements Result<T> {}
    record Failure<T>(String error) implements Result<T> {}
}

// Exhaustive pattern matching on sealed type
static <T> String describe(Result<T> result) {
    return switch (result) {
        case Result.Success<T> s -> "OK: " + s.value();
        case Result.Failure<T> f -> "Error: " + f.error();
    };
}

// Builder pattern — for complex object construction
public class User {
    private final String name;
    private final String email;
    private final String phone;

    private User(Builder b) {
        this.name  = b.name;
        this.email = b.email;
        this.phone = b.phone;
    }

    public static class Builder {
        private String name, email, phone;
        public Builder name(String v)  { this.name = v; return this; }
        public Builder email(String v) { this.email = v; return this; }
        public Builder phone(String v) { this.phone = v; return this; }
        public User build() { return new User(this); }
    }
}

// Usage: fluent API
User user = new User.Builder()
    .name("Priya")
    .email("priya@example.com")
    .phone("+91-98765-43210")
    .build();

Enums

Javaenums.java
// Enum — a fixed set of constants, each is an instance of the enum class
public enum DayOfWeek {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;

    public boolean isWeekend() {
        return this == SATURDAY || this == SUNDAY;
    }
}

// Enum with fields and methods
public enum Planet {
    MERCURY(3.303e+23, 2.4397e6),
    VENUS  (4.869e+24, 6.0518e6),
    EARTH  (5.976e+24, 6.37814e6);

    private final double mass, radius;
    static final double G = 6.67300E-11;

    Planet(double mass, double radius) {
        this.mass = mass; this.radius = radius;
    }

    double surfaceGravity() { return G * mass / (radius * radius); }
    double surfaceWeight(double otherMass) { return otherMass * surfaceGravity(); }
}

System.out.println(Planet.EARTH.surfaceWeight(75));   // 735.0 N
System.out.println(DayOfWeek.SATURDAY.isWeekend());   // true
Commonly confused
Interfaces and abstract classes are not interchangeable. An abstract class can have state (fields), constructors, and concrete methods. A class can only extend one abstract class. An interface (before Java 8) had only abstract methods; a class can implement many interfaces. Rule of thumb: use interfaces for behaviour contracts, abstract classes for partial implementations with shared state.
@Override is not optional — always use it. Without @Override, if you misspell a method name you intend to override, the compiler silently treats it as a new method instead. With @Override, the compiler verifies the method actually overrides something — turning silent bugs into compile errors.
equals() and hashCode() must be consistent. If two objects are equal by equals(), they must have the same hashCode(). If you override one, override both. Violating this contract breaks HashMap, HashSet, and any collection using hashing.
How this connects
Requires first
Primary source

The Java object model and vtable dispatch

Java uses nominal typing — types must explicitly declare their relationships (extends, implements). The JVM uses vtables for virtual dispatch: each class has a vtable (array of method pointers for overridable methods) and each object instance carries a pointer to its class's vtable. Virtual method invocation is an indirect call through the vtable — one pointer dereference. The JIT compiler (HotSpot) performs inline caching and devirtualisation: if the JIT observes that a call site always dispatches to the same concrete type, it replaces the vtable dispatch with a direct inlined call.

Project Loom — Virtual Threads (Java 21)

Java 21 introduced virtual threads (Project Loom) as a production-stable feature (JEP 444). Virtual threads are lightweight user-mode threads scheduled by the JVM, not the OS. Creating a million virtual threads is practical; creating a million OS threads is not. When a virtual thread blocks on I/O, the JVM unmounts it from the carrier (OS) thread, freeing the carrier to run other virtual threads. This transforms blocking I/O code into highly concurrent code without changing the programming model — you write straightforward blocking code and get the throughput of reactive/async code.

Sources

1
Java Language Specification §8 — Classes. docs.oracle.com/javase/specs/jls/se21/html/jls-8.html.
2
Java Language Specification §9 — Interfaces. docs.oracle.com/javase/specs/jls/se21/html/jls-9.html.
3
JEP 395 — Records. openjdk.org/jeps/395.
4
JEP 444 — Virtual Threads. openjdk.org/jeps/444.
Source confidence: HighLast verified: Primary source: Java SE Documentation · docs.oracle.com/en/java/

Sources

1
JLS §8 — Classes. docs.oracle.com/javase/specs/jls/se21/html/jls-8.html.
2
JLS §9 — Interfaces. docs.oracle.com/javase/specs/jls/se21/html/jls-9.html.
3
JEP 395 — Records. openjdk.org/jeps/395.
4
JEP 444 — Virtual Threads. openjdk.org/jeps/444.