Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Hoofdstuk 11 — OOP: Klassen en Objecten (basis van objectgeoriënteerd denken)

In dit hoofdstuk maak je de stap van losse functies en gegevens naar samenhangende eenheden: objecten. Je leert wat klassen en objecten zijn, hoe je velden en methoden definieert, hoe constructors werken, wat this doet, en waarom inkapseling (encapsulation) met getters/setters nuttig is. We bekijken ook toString, equals/hashCode en geven praktische tips.

Voorbeeld 1 — Een eenvoudige klasse met veld en methode

class Persoon {
    String naam;                 // veld (instantievariabele)
    void groet() {               // instantie-methode
        System.out.println("Hallo, " + naam);
    }
}
public class Demo1 {
    public static void main(String[] args) {
        Persoon p = new Persoon(); // object maken
        p.naam = "Saar";          // veld rechtstreeks zetten (later: encapsuleren)
        p.groet();                 // methode aanroepen
    }
}

Voorbeeld 2 — Constructor voor verplichte initialisatie

class Punt {
    int x, y;
    Punt(int x, int y) {         // constructor
        this.x = x;              // this verwijst naar het huidige object
        this.y = y;
    }
}
public class Demo2 {
    public static void main(String[] args) {
        Punt p = new Punt(3, 4);
        System.out.println("(" + p.x + ", " + p.y + ")");
    }
}

Voorbeeld 3 — Encapsulation met private velden en getters/setters

class BankRekening {
    private String iban;     // afschermen voor directe toegang
    private double saldo;

    public BankRekening(String iban) {
        this.iban = iban;
    }
    public String getIban() { return iban; }
    public double getSaldo() { return saldo; }
    public void stort(double bedrag) {
        if (bedrag <= 0) throw new IllegalArgumentException("Bedrag moet > 0");
        saldo += bedrag;
    }
    public void neemOp(double bedrag) {
        if (bedrag <= 0 || bedrag > saldo) throw new IllegalArgumentException("Ongeldig bedrag");
        saldo -= bedrag;
    }
}
public class Demo3 {
    public static void main(String[] args) {
        BankRekening r = new BankRekening("NL00BANK0123456789");
        r.stort(100);
        r.neemOp(30);
        System.out.println("Saldo: " + r.getSaldo());
    }
}

Voorbeeld 4 — toString() voor leesbare weergave

class Product {
    private final String naam;
    private final double prijs;
    Product(String naam, double prijs) {
        this.naam = naam; this.prijs = prijs;
    }
    @Override public String toString() {
        return "Product{" + "naam='" + naam + '\'' + ", prijs=" + prijs + '}';
    }
}
public class Demo4 {
    public static void main(String[] args) {
        System.out.println(new Product("Mok", 7.95));
    }
}

Voorbeeld 5 — equals en hashCode voor waardevergelijking

import java.util.Objects;
class Kaart {
    private final String soort;  // bijv. harten, schoppen
    private final String rang;   // bijv. boer, vrouw, aas
    Kaart(String soort, String rang) {
        this.soort = soort; this.rang = rang;
    }
    @Override public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Kaart kaart = (Kaart) o;
        return Objects.equals(soort, kaart.soort) && Objects.equals(rang, kaart.rang);
    }
    @Override public int hashCode() {
        return Objects.hash(soort, rang);
    }
}
public class Demo5 {
    public static void main(String[] args) {
        System.out.println(new Kaart("harten", "aas").equals(new Kaart("harten", "aas"))); // true
    }
}

Voorbeeld 6 — Statische leden vs. instantieleden

class Teller {
    static int aantalObjecten = 0; // gedeeld door alle instanties
    int waarde;                    // per object verschillend
    Teller(int waarde) {
        this.waarde = waarde;
        aantalObjecten++;
    }
}
public class Demo6 {
    public static void main(String[] args) {
        Teller a = new Teller(1);
        Teller b = new Teller(2);
        System.out.println(Teller.aantalObjecten); // 2
        System.out.println(a.waarde + ", " + b.waarde);
    }
}

Voorbeeld 7 — Samenspel van objecten (compositie)

class Adres { String straat; String stad; Adres(String straat, String stad){this.straat=straat;this.stad=stad;} }
class Klant {
    String naam;
    Adres adres; // Klant heeft een Adres
    Klant(String naam, Adres adres) { this.naam = naam; this.adres = adres; }
}
public class Demo7 {
    public static void main(String[] args) {
        Adres a = new Adres("Laan 1", "Utrecht");
        Klant k = new Klant("Kim", a);
        System.out.println(k.naam + " uit " + k.adres.stad);
    }
}

Voorbeeld 8 — Method chaining en builder-achtig patroon

class BerichtBuilder {
    private final StringBuilder sb = new StringBuilder();
    BerichtBuilder voegToe(String s) { sb.append(s); return this; }
    String build() { return sb.toString(); }
}
public class Demo8 {
    public static void main(String[] args) {
        String bericht = new BerichtBuilder().voegToe("Hallo").voegToe(", ").voegToe("wereld").build();
        System.out.println(bericht);
    }
}

Veelgemaakte fouten en tips:

  • Gebruik private voor velden en exposeer gedrag via methoden; vermijd publieke velden tenzij goede reden.
  • Vergeet this niet wanneer parameternaam gelijk is aan veldnaam.
  • Herdefinieer toString voor debugbaarheid; overweeg equals/hashCode voor waardeobjecten.
  • Maak onveranderlijke (immutable) objecten waar mogelijk: gebruik final velden en geen setters.

Koppeling met oefeningen:

  • Zie java-workbook/src/chapter_11.md voor opdrachten over klassen/objecten, constructors en encapsulation.
  • Uitwerkingen staan in java-solutions/src/chapter_11.md en bijbehorende .java-bestanden.