BeginnerJava · Lesson 6 of 8

Classes & Objects

Fields, constructors, methods, encapsulation with private fields, and toString.

A class is a blueprint; objects are instances created with new. Fields hold each object's data, constructors set it up, and instance methods act on it using this.

Encapsulation: make fields private and expose behaviour through public methods, so the class can protect its rules (such as "a score must be 0–100"). Throw an exception such as IllegalArgumentException when a rule is broken.

Override toString() to control how an object prints. static members belong to the class rather than to each object — like a shared pass mark.

School.javaJava
import java.util.ArrayList;
import java.util.List;

public class School {
    public static void main(String[] args) {
        Student amina = new Student("Amina Hassan", 4);
        amina.addScore(88);
        amina.addScore(79);

        Student juma = new Student("Juma Said", 3);
        juma.addScore(25);

        System.out.println(amina);
        System.out.println(juma);
        System.out.println("Pass mark: " + Student.PASS_MARK);

        try {
            juma.addScore(150);
        } catch (IllegalArgumentException e) {
            System.out.println("Rejected: " + e.getMessage());
        }
    }
}

class Student {
    static final int PASS_MARK = 30;

    private final String name;
    private final int form;
    private final List<Integer> scores = new ArrayList<>();

    Student(String name, int form) {
        this.name = name;
        this.form = form;
    }

    void addScore(int score) {
        if (score < 0 || score > 100) {
            throw new IllegalArgumentException("score must be 0-100, got " + score);
        }
        scores.add(score);
    }

    double average() {
        if (scores.isEmpty()) return 0;
        int total = 0;
        for (int s : scores) total += s;
        return (double) total / scores.size();
    }

    boolean passed() {
        return average() >= PASS_MARK;
    }

    String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "%s (Form %d): average %.1f - %s".formatted(name, form, average(), passed() ? "PASS" : "FAIL");
    }
}

Key points

  • Keep fields private; enforce rules inside methods.
  • Constructors set up valid objects; this refers to the current object.
  • Override toString() for readable output; static members are shared by all objects.

Exercise

Create a BankAccount class with a private balance, deposit and withdraw methods that reject invalid amounts, and a toString. Create two accounts in main, transfer money between them and print both.

Show solution

Try the exercise yourself first — then compare your approach with this one.

The balance is private, so it can only change through deposit and withdraw, which reject invalid amounts. A transfer is simply a withdrawal from one account followed by a deposit to the other.

Bank.javaJava
public class Bank {
    public static void main(String[] args) {
        BankAccount amina = new BankAccount("Amina");
        BankAccount juma = new BankAccount("Juma");

        amina.deposit(100_000);
        amina.withdraw(30_000);
        juma.deposit(amina.withdraw(25_000));   // transfer 25,000 from Amina to Juma

        System.out.println(amina);   // Amina: 45000 TSh
        System.out.println(juma);    // Juma: 25000 TSh

        try {
            juma.withdraw(50_000);
        } catch (IllegalArgumentException e) {
            System.out.println("Rejected: " + e.getMessage());
        }
    }
}

class BankAccount {
    private final String owner;
    private long balance;

    BankAccount(String owner) {
        this.owner = owner;
    }

    void deposit(long amount) {
        if (amount <= 0) throw new IllegalArgumentException("deposit must be positive");
        balance += amount;
    }

    long withdraw(long amount) {
        if (amount <= 0) throw new IllegalArgumentException("withdrawal must be positive");
        if (amount > balance) throw new IllegalArgumentException(owner + " has only " + balance + " TSh");
        balance -= amount;
        return amount;
    }

    @Override
    public String toString() {
        return owner + ": " + balance + " TSh";
    }
}

Check your understanding

  1. Why make a class's fields private?

  2. What does the keyword this refer to inside an instance method?

  3. What does a static field such as PASS_MARK belong to?

  4. What happens if you don't override toString() and print an object?

Ask AI