Lesson 6 of 13

Classes & OOP

Learn how to define classes, create objects, and use constructors, inheritance and overriding. Classes are how almost all Java code is organised.

Java
// Abstract class
public abstract class Animal {
    private String name;  // private — encapsulation

    public Animal(String name) { this.name = name; }

    public String getName() { return name; }  // getter

    public abstract String speak();  // subclasses must implement

    public String describe() {  // can be overridden
        return name + " says " + speak();
    }
}

// Interface
public interface Trainable {
    void learn(String trick);
    List<String> getTricks();

    default String showTricks() {  // default implementation (Java 8+)
        return String.join(", ", getTricks());
    }
}

// Concrete class
public class Dog extends Animal implements Trainable {
    private List<String> tricks = new ArrayList<>();

    public Dog(String name) { super(name); }

    @Override
    public String speak() { return "Woof!"; }

    @Override
    public void learn(String trick) { tricks.add(trick); }

    @Override
    public List<String> getTricks() { return tricks; }

    @Override
    public String describe() {
        return super.describe() + " (knows " + tricks.size() + " tricks)";
    }
}

// Java 16+ records — immutable data classes
record Point(double x, double y) {
    // Compact constructor for validation
    Point {
        if (Double.isNaN(x) || Double.isNaN(y))
            throw new IllegalArgumentException("Coordinates cannot be NaN");
    }

    public double distance() {
        return Math.sqrt(x * x + y * y);
    }
}
Java
// Generics
public class Pair<A, B> {
    private final A first;
    private final B second;

    public Pair(A first, B second) {
        this.first = first;
        this.second = second;
    }

    public A getFirst() { return first; }
    public B getSecond() { return second; }

    @Override
    public String toString() {
        return "(" + first + ", " + second + ")";
    }
}

// Generic method
public static <T extends Comparable<T>> T max(T a, T b) {
    return a.compareTo(b) >= 0 ? a : b;
}

// Main usage
public static void main(String[] args) {
    Dog dog = new Dog("Rex");
    dog.learn("sit");
    dog.learn("shake");
    System.out.println(dog.describe());
    System.out.println(dog.showTricks());

    Point p = new Point(3, 4);
    System.out.println("Distance: " + p.distance());

    Pair<String, Integer> pair = new Pair<>("Alice", 30);
    System.out.println(pair);

    System.out.println(max("apple", "banana"));   // banana
    System.out.println(max(42, 17));               // 42
}

Common question

Does Java pass objects by reference?

A Java of a type holds a to an object, not the object itself. Assignment copies the reference, so two variables can point at one object. Passing to a copies the reference too, so the method can change the object but cannot repoint the caller's variable. Primitives hold their values directly and are copied outright.

Two rules prevent most beginner : compare objects with equals (and override it together with hashCode), and be deliberate about whether a method changes its or returns a new value. classes, like and the newer records, make both questions go away.

Source: The Java Language Specification says that when a method is invoked, the values of the actual argument expressions initialise newly created parameter variables. Java copies values; it never passes the caller's variable itself.

Common mix-ups

Each of these sounds reasonable. Think about why it is wrong, then open the explanation.

  • Changing an object's field inside a method changes it for the caller, so that's pass by reference.

  • Comparing two strings with == compares their characters.

  • An is just a class with no implementation.

Finished reading?Mark it done to track your progress.

Tip: use the ← and → keys to move between lessons.