Skip to main content

Object-Oriented Programming (OOP)

Java is an OOP language at its core. Everything is an object (except primitives). OOP helps organize complex software by modeling it after real-world entities.

1. Classes & Objects

A Class is a blueprint or template. An Object is a specific instance created from that blueprint.
  • Class: Car (Has wheels, engine, color)
  • Object: Tesla Model 3 (Red, Electric)
public class Car {
    // Fields (State): Data stored in the object
    private String brand;
    private int speed;

    // Constructor: Called when creating the object
    public Car(String brand) {
        this.brand = brand;
        this.speed = 0;
    }

    // Methods (Behavior): Actions the object can perform
    public void accelerate(int amount) {
        this.speed += amount;
    }
}

// Usage
Car tesla = new Car("Tesla"); // Create new object on Heap
tesla.accelerate(100);        // Call method

Access Modifiers

Control who can see and use your code. This is Encapsulation.
ModifierClassPackageSubclassWorld
publicYesYesYesYes
protectedYesYesYesNo
defaultYesYesNoNo
privateYesNoNoNo
Best Practice: Always make fields private. Expose them via public methods (getters/setters) only if necessary. This protects your data from invalid states.

2. Inheritance

Inheritance allows a class to acquire properties of another. It promotes code reuse. Java supports single inheritance (a class can extend only one parent).
// Base Class (Superclass)
public class Animal {
    protected String name;
    
    public Animal(String name) {
        this.name = name;
    }
    
    public void makeSound() {
        System.out.println("Generic sound");
    }
}

// Derived Class (Subclass)
public class Dog extends Animal {
    public Dog(String name) {
        super(name); // Call super constructor
    }
    
    @Override // Annotation ensures we are correctly overriding
    public void makeSound() {
        System.out.println("Woof!");
    }
}

3. Polymorphism

Polymorphism (“many forms”) allows you to treat objects of different subclasses as objects of their superclass. This makes your code flexible.
Animal myDog = new Dog("Buddy");
myDog.makeSound(); // Prints "Woof!" 
Even though the variable type is Animal, Java knows at runtime that the actual object is a Dog, so it calls the Dog version of makeSound. This is called Dynamic Dispatch.

Abstract Classes

An abstract class cannot be instantiated. It’s a partial blueprint that forces subclasses to complete the implementation.
public abstract class Shape {
    abstract double area(); // Subclasses MUST implement this
}

4. Interfaces

Interfaces define a contract. They say “I promise to do X”, but they don’t say how. A class can implement multiple interfaces.
interface Flyable {
    void fly();
    
    // Default method (Java 8+): Provides a default implementation
    default void land() {
        System.out.println("Landing...");
    }
}

public class Bird implements Flyable {
    public void fly() {
        System.out.println("Flapping wings");
    }
}

5. Records (Java 14+)

For years, Java developers wrote boilerplate code for classes that just held data (getters, equals, hashCode, toString). Records solve this. They are immutable data carriers. Before (Boilerplate):
class Point {
    private final int x;
    private final int y;
    public Point(int x, int y) { this.x = x; this.y = y; }
    public int x() { return x; }
    public int y() { return y; }
    // ... plus 50 lines of equals, hashCode, toString ...
}
After (Record):
public record Point(int x, int y) {}
That’s it. The compiler generates everything for you. Usage:
Point p = new Point(10, 20);
System.out.println(p.x()); // 10
System.out.println(p);     // Point[x=10, y=20]

6. Sealed Classes (Java 17+)

Sealed classes give you control over your hierarchy. You can specify exactly which classes are allowed to extend your class.
public sealed interface Shape
    permits Circle, Rectangle, Square {
    // Only these classes can implement Shape
}
Why? It allows the compiler to know every possible subtype. This is useful for pattern matching, ensuring you handle every case.

Summary

  • Classes: Encapsulate state and behavior.
  • Inheritance: extends for code reuse.
  • Polymorphism: Treat subclasses as superclasses.
  • Interfaces: Define contracts (implements).
  • Records: Concise, immutable data classes. Use them for DTOs.
  • Sealed Classes: Restricted inheritance hierarchies.
Next, we’ll explore the Collections Framework, where we store and manipulate groups of objects.