MINT lernen

Übung — AFB II (Zusammenhänge herstellen)

Zehn mehrschrittige Aufgaben: Diagramme erstellen, Klassen implementieren, Referenzen verfolgen und Reihungen von Objekten auswerten.

Dein Fortschritt:
0 / 0 Aufgaben
2

Aufgabenblock — AFB II

Zehn mehrschrittige Aufgaben, bunt gemischt aus allen Unterkapiteln: Klassendiagramme aus Texten erstellen, Klassen und Methoden mit Schleifen implementieren, Aufrufe mit Referenzen und Aliasen verfolgen, Reihungen von Objekten auswerten und Fehler im Quelltext finden. Die gestuften Tipps helfen, wenn du nicht weiterkommst.

A1
Klassendiagramm aus einem Text: Stadtbibliothek
AFB II

Eine Stadtbibliothek beschreibt ihre Anforderungen so:

„Jedes Medium hat eine Signatur und einen Titel; beides ändert sich nach dem Erfassen nicht mehr. Man muss abfragen können, ob ein Medium ausgeliehen ist und von wem. Ein Leser hat eine Ausweisnummer und einen Namen. Er darf höchstens fünf Medien gleichzeitig ausleihen. Ein Leser kann ein Medium ausleihen und zurückgeben; außerdem soll man abfragen können, wie viele Medien er gerade hat.“

Erstellen Sie ein Klassendiagramm mit den Klassen Medium und Leser (Attribute, Konstruktoren, Methoden, Assoziation mit Kardinalitäten).

Ansatz: Markiere Substantive (Kandidaten für Klassen und Attribute) und Verben (Kandidaten für Methoden). „Ändert sich nicht“ heißt: Getter ja, Setter nein.
Beziehung: „von wem“ bedeutet: Ein Medium kennt seinen Leser (oder keinen → null). „höchstens fünf Medien“ spricht für eine Reihung vom Typ Medium mit Länge 5 und einen Zähler.
Kardinalitäten: Wie viele Leser hat ein Medium gleichzeitig höchstens? Wie viele Medien ein Leser?
Vollständige Lösung
Leser
  • - ausweisNr: Ganzzahl
  • - name: Zeichenkette
  • - medien: Reihung vom Typ Medium
  • - anzahl: Ganzzahl
  • c Leser(ausweisNr: Ganzzahl, name: Zeichenkette)
  • + ausleihen(m: Medium): Wahrheitswert
  • + zurueckgeben(m: Medium)
  • + getAnzahl(): Ganzzahl
leiht aus0..10..5
Medium
  • - signatur: Zeichenkette
  • - titel: Zeichenkette
  • - ausleiher: Leser
  • c Medium(signatur: Zeichenkette, titel: Zeichenkette)
  • + getSignatur(): Zeichenkette
  • + getTitel(): Zeichenkette
  • + istAusgeliehen(): Wahrheitswert
  • + getAusleiher(): Leser
  • + setAusleiher(l: Leser)

Begründungen: Signatur und Titel bekommen keinen Setter (nur lesbar). „Ausgeliehen“ braucht kein eigenes Attribut: istAusgeliehen() liefert ausleiher != null — so können die beiden Angaben nie widersprüchlich werden. ausleihen liefert einen Wahrheitswert, weil es scheitern kann (schon fünf Medien oder Medium schon verliehen). Ein Medium ist bei höchstens einem Leser (0..1), ein Leser hat 0 bis 5 Medien.

So könnte Leser implementiert werden:

public class Leser {
    private int ausweisNr;
    private String name;
    private Medium[] medien;
    private int anzahl;

    public Leser(int ausweisNr, String name) {
        this.ausweisNr = ausweisNr;
        this.name = name;
        medien = new Medium[5];
        anzahl = 0;
    }

    public boolean ausleihen(Medium m) {
        if (anzahl < medien.length && !m.istAusgeliehen()) {
            medien[anzahl] = m;
            anzahl = anzahl + 1;
            m.setAusleiher(this);
            return true;
        }
        return false;
    }

    public void zurueckgeben(Medium m) {
        for (int i = 0; i < anzahl; i++) {
            if (medien[i] == m) {
                medien[i] = medien[anzahl - 1];
                medien[anzahl - 1] = null;
                anzahl = anzahl - 1;
                m.setAusleiher(null);
                return;
            }
        }
    }

    public int getAnzahl() {
        return anzahl;
    }
}
A2
Klasse implementieren: Parkticket
AFB II

Ein Parkhaus rechnet so ab: Die ersten 15 Minuten sind kostenlos. Danach kostet jede angefangene halbe Stunde 150 Cent, höchstens aber 1200 Cent. Zeiten werden in Minuten seit Mitternacht gespeichert (8:40 Uhr = 520).

Parkticket
  • - einfahrt: Ganzzahl
  • - bezahlt: Wahrheitswert
  • c Parkticket(einfahrt: Ganzzahl)
  • + getEinfahrt(): Ganzzahl
  • + berechneGebuehr(ausfahrt: Ganzzahl): Ganzzahl
  • + bezahlen()
  • + istBezahlt(): Wahrheitswert

Implementieren Sie die Klasse. Ermitteln Sie außerdem die Gebühr in Cent für ein Ticket mit Einfahrt 8:40 Uhr bei Ausfahrt um 10:15 Uhr bzw. um 16:40 Uhr.

Cent
Cent
Ansatz: In berechneGebuehr zuerst die Parkdauer ausfahrt − einfahrt bestimmen, dann die drei Regeln nacheinander anwenden.
Angefangene halbe Stunden: Mit Ganzzahldivision rundet dauer / 30 ab. Der Trick (dauer + 29) / 30 rundet auf: 95 Minuten → 124 / 30 = 4 Takte.
Zahlen: 10:15 Uhr = 615, 16:40 Uhr = 1000. Vergiss die Deckelung nicht.
Vollständige Lösung
public class Parkticket {
    private int einfahrt;
    private boolean bezahlt;

    public Parkticket(int einfahrt) {
        this.einfahrt = einfahrt;
        bezahlt = false;
    }

    public int getEinfahrt() {
        return einfahrt;
    }

    public int berechneGebuehr(int ausfahrt) {
        int dauer = ausfahrt - einfahrt;
        if (dauer <= 15) {
            return 0;
        }
        int takte = (dauer + 29) / 30;
        int gebuehr = takte * 150;
        if (gebuehr > 1200) {
            gebuehr = 1200;
        }
        return gebuehr;
    }

    public void bezahlen() {
        bezahlt = true;
    }

    public boolean istBezahlt() {
        return bezahlt;
    }
}

10:15 Uhr: Dauer 615 − 520 = 95 min → 4 angefangene halbe Stunden → 4 · 150 = 600 Cent.
16:40 Uhr: Dauer 480 min → 16 Takte → 2400 Cent, gedeckelt auf 1200 Cent.

Wer mit dauer / 30 rechnet, erhält fälschlich 3 Takte (450 Cent). Einen Setter für einfahrt gibt es bewusst nicht — die Einfahrtszeit darf nachträglich nicht manipulierbar sein.

A3
Aufrufe mit Referenzen verfolgen: Sportverein
AFB II

In der Mitgliederverwaltung eines Sportvereins senkt ermaessigen(prozent) den Jahresbeitrag (in Euro) mit der Anweisung beitrag = beitrag - beitrag * prozent / 100;

Mitglied a = new Mitglied("Ole", 120);
Mitglied b = new Mitglied("Ole", 120);
Mitglied c = a;
c.ermaessigen(25);
b = c;
b.ermaessigen(10);
int x = a.getBeitrag();
x = x - 1;
boolean gleich = (a == b);

Stellen Sie den Ablauf in einer Tracetabelle dar (Variablen und Objektzustände) und geben Sie die Endwerte an. Wie viele Mitglied-Objekte werden erzeugt, und welchen Wert hat gleich?

€
Ansatz: Nummeriere die Objekte (Objekt 1 aus Zeile 1, Objekt 2 aus Zeile 2) und notiere für a, b, c jeweils, auf welches Objekt sie zeigen.
Zeile 5: b = c kopiert die Referenz. Danach zeigen alle drei Variablen auf Objekt 1; Objekt 2 ist nicht mehr erreichbar.
Rechnen: 120 − 120 · 25 / 100 = 90; danach 90 − 90 · 10 / 100 = 81 (Ganzzahldivision).
Vollständige Lösung
Mitglied a = new Mitglied("Ole", 120);  // a -> O1 (120)
Mitglied b = new Mitglied("Ole", 120);  // b -> O2 (120)
Mitglied c = a;                         // c -> O1
c.ermaessigen(25);                      // O1: 90
b = c;                                  // b -> O1, O2 unerreichbar
b.ermaessigen(10);                      // O1: 81
int x = a.getBeitrag();                 // x = 81 (Kopie des Werts)
x = x - 1;                              // x = 80, O1 bleibt 81
boolean gleich = (a == b);              // true: gleiche Referenz

Endwerte: a.getBeitrag() = 81, x = 80, es wurden 2 Objekte erzeugt, gleich ist true. Hätte man a == b direkt nach Zeile 2 geprüft, wäre das Ergebnis false gewesen — trotz gleicher Attributwerte.

A4
Reihung von Objekten auswerten: Wetterstation
AFB II

Eine Schul-Wetterstation speichert die Messwerte eines Tages. Weil der Sensor manchmal ausfällt, bleiben Plätze leer.

Messwert[] tag = new Messwert[6];
tag[0] = new Messwert(6, 3.5);    // (stunde, temperatur)
tag[1] = new Messwert(9, 7.0);
tag[3] = new Messwert(15, 12.5);
tag[4] = new Messwert(18, 9.0);

double summe = 0;
int anzahl = 0;
double max = -100;
int maxStunde = -1;
for (int i = 0; i < tag.length; i++) {
    if (tag[i] != null) {
        summe = summe + tag[i].getTemperatur();
        anzahl = anzahl + 1;
        if (tag[i].getTemperatur() > max) {
            max = tag[i].getTemperatur();
            maxStunde = tag[i].getStunde();
        }
    }
}
double mittel = summe / anzahl;

Analysieren Sie den Quelltext: Welche Werte haben mittel und maxStunde am Ende? Erläutern Sie, was ohne die Prüfung tag[i] != null passieren würde.

°C
Uhr
Ansatz: Gehe i von 0 bis 5 durch. Bei den Indizes 2 und 5 steht null — diese Durchläufe überspringt die if-Anweisung.
Mittelwert: Geteilt wird durch anzahl (belegte Plätze), nicht durch tag.length.
Vollständige Lösung

Belegte Plätze: 0, 1, 3, 4. summe = 3,5 + 7,0 + 12,5 + 9,0 = 32,0; anzahl = 4 → mittel = 8,0 °C. Die höchste Temperatur 12,5 °C wurde um 15 Uhr gemessen, also maxStunde = 15.

Ohne Null-Prüfung würde bei i = 2 der Aufruf tag[2].getTemperatur() auf null erfolgen → NullPointerException, das Programm bricht ab. Wer durch tag.length teilt, erhält fälschlich 5,33 °C.

A5
Methoden mit Schleife: Zug und Wagen
AFB II

Ein Reservierungssystem verwaltet die Wagen eines Zuges. Die Klasse Wagen ist fertig.

Zug
  • - wagen: Reihung vom Typ Wagen
  • - anzahl: Ganzzahl
  • c Zug(maxWagen: Ganzzahl)
  • + anhaengen(w: Wagen): Wahrheitswert
  • + getFreiePlaetze(): Ganzzahl
  • + findeWagenMitPlatz(n: Ganzzahl): Wagen
besteht aus1n
Wagen
  • - nummer: Ganzzahl
  • - plaetze: Ganzzahl
  • - belegt: Ganzzahl
  • c Wagen(nummer: Ganzzahl, plaetze: Ganzzahl, belegt: Ganzzahl)
  • + getNummer(): Ganzzahl
  • + getPlaetze(): Ganzzahl
  • + getFrei(): Ganzzahl

Implementieren Sie die Klasse Zug. anhaengen liefert false, wenn der Zug voll ist; findeWagenMitPlatz(n) liefert den ersten Wagen mit mindestens n freien Plätzen, sonst null.

Geben Sie für einen Zug mit den Wagen 21 (60 Plätze, 58 belegt), 22 (80 Plätze, 71 belegt) und 23 (60 Plätze, 12 belegt) die Rückgabe von getFreiePlaetze() und die Nummer des Wagens an, den findeWagenMitPlatz(5) liefert.

Ansatz: Die Wagen stehen lückenlos auf den Plätzen 0 bis anzahl − 1. Die Schleife läuft deshalb bis anzahl, nicht bis wagen.length — so brauchst du keine Null-Prüfung.
Suchen: Sobald ein passender Wagen gefunden ist, kannst du ihn sofort mit return zurückgeben. return null; steht erst nach der Schleife.
Zahlen: Freie Plätze je Wagen: 2, 9, 48.
Vollständige Lösung
public class Zug {
    private Wagen[] wagen;
    private int anzahl;

    public Zug(int maxWagen) {
        wagen = new Wagen[maxWagen];
        anzahl = 0;
    }

    public boolean anhaengen(Wagen w) {
        if (anzahl < wagen.length) {
            wagen[anzahl] = w;
            anzahl = anzahl + 1;
            return true;
        }
        return false;
    }

    public int getFreiePlaetze() {
        int summe = 0;
        for (int i = 0; i < anzahl; i++) {
            summe = summe + wagen[i].getFrei();
        }
        return summe;
    }

    public Wagen findeWagenMitPlatz(int n) {
        for (int i = 0; i < anzahl; i++) {
            if (wagen[i].getFrei() >= n) {
                return wagen[i];
            }
        }
        return null;
    }
}

Freie Plätze: 2 + 9 + 48 = 59. findeWagenMitPlatz(5): Wagen 21 hat nur 2 freie Plätze, Wagen 22 hat 9 → Rückgabe ist Wagen 22 (nicht der mit den meisten freien Plätzen, sondern der erste passende). findeWagenMitPlatz(50) liefert null.

Typischer Fehler: return null; im else-Zweig innerhalb der Schleife — dann wird nur der erste Wagen geprüft.

A6
Fehler finden: Pizzeria-Bestellung
AFB II

Ein Mitschüler hat für die Bestellsoftware einer Pizzeria diese Klasse geschrieben. Sie enthält sechs Fehler — manche verhindern das Kompilieren, andere führen zu falschem Verhalten oder verletzen die Datenkapselung.

public class Bestellung {
    public String kunde;
    private int anzahlPizzen;
    private boolean geliefert;

    public void Bestellung(String kunde) {
        kunde = kunde;
        anzahlPizzen = 0;
    }

    public void pizzaHinzufuegen(int anzahl) {
        if (anzahl > 0) {
            anzahlPizzen = anzahlPizzen + anzahl;
        }
    }

    public int getAnzahlPizzen() {
        anzahlPizzen;
    }

    public boolean istFuer(String name) {
        return kunde == name;
    }

    public void liefern() {
        geliefert = true;
    }
}

// in einer Testklasse:
Bestellung b;
b.pizzaHinzufuegen(2);

Überprüfen Sie den Quelltext: Benennen Sie die sechs Fehler, ordnen Sie sie ein (Syntaxfehler / Laufzeit- oder Logikfehler / Verstoß gegen die Kapselung) und korrigieren Sie sie.

Ansatz: Gehe den Quelltext mit einer Checkliste durch: Sichtbarkeit der Attribute, Konstruktor, this, return, Vergleich von Zeichenketten, Objekterzeugung.
Konstruktor: Mit void ist Bestellung(...) eine gewöhnliche Methode. Was bedeutet das für new Bestellung("Ada")?
Testklasse: Eine Variable vom Typ Bestellung ist nur ein Verweis. Worauf zeigt b?
Vollständige Lösung
  1. public String kunde; — Kapselung verletzt: Attribute sind privat.
  2. public void Bestellung(...) — mit void ist das kein Konstruktor. new Bestellung("Ada") scheitert beim Kompilieren, weil es keinen passenden Konstruktor gibt.
  3. kunde = kunde; — Logikfehler: Der Parameter überdeckt das Attribut, das Attribut bleibt null. Richtig: this.kunde = kunde;
  4. anzahlPizzen; ohne return — Syntaxfehler („not a statement“, fehlende Rückgabe).
  5. kunde == name — Logikfehler: vergleicht Referenzen, nicht Inhalte. Richtig: kunde.equals(name)
  6. Bestellung b; ohne new — Syntaxfehler (Variable nicht initialisiert); mit Bestellung b = null; wäre es eine NullPointerException zur Laufzeit.

Korrigierte Fassung:

public class Bestellung {
    private String kunde;
    private int anzahlPizzen;
    private boolean geliefert;

    public Bestellung(String kunde) {
        this.kunde = kunde;
        anzahlPizzen = 0;
        geliefert = false;
    }

    public void pizzaHinzufuegen(int anzahl) {
        if (anzahl > 0) {
            anzahlPizzen = anzahlPizzen + anzahl;
        }
    }

    public int getAnzahlPizzen() {
        return anzahlPizzen;
    }

    public boolean istFuer(String name) {
        return kunde.equals(name);
    }

    public void liefern() {
        geliefert = true;
    }
}
Bestellung b = new Bestellung("Ada");
b.pizzaHinzufuegen(2);
A7
Assoziation mit null: Kfz-Werkstatt
AFB II

In einer Werkstatt kann ein Auftrag einem Mechaniker zugewiesen sein — oder noch keinem.

Auftrag
  • - beschreibung: Zeichenkette
  • - kennzeichen: Zeichenkette
  • - mechaniker: Mechaniker
  • - erledigt: Wahrheitswert
  • c Auftrag(beschreibung: Zeichenkette, kennzeichen: Zeichenkette)
bearbeitet vonn0..1
Mechaniker
  • - name: Zeichenkette
  • - offeneAuftraege: Ganzzahl
  • c Mechaniker(name: Zeichenkette)
  • + getName(): Zeichenkette
  • + getOffeneAuftraege(): Ganzzahl
  • + auftragUebernehmen()
  • + auftragAbschliessen()

Erweitern Sie die Klasse Auftrag um:

  • + zuweisen(m: Mechaniker): Wahrheitswert — nur möglich, wenn noch kein Mechaniker zugewiesen ist; der Mechaniker übernimmt dann den Auftrag.
  • + getMechanikerName(): Zeichenkette — liefert „nicht zugewiesen“, wenn es keinen Mechaniker gibt.
  • + abschliessen() — nur ein zugewiesener, noch nicht erledigter Auftrag kann abgeschlossen werden.

Geben Sie an, welchen Wert m.getOffeneAuftraege() nach diesen Anweisungen liefert:

Mechaniker m = new Mechaniker("Deniz");
Auftrag a1 = new Auftrag("Bremsen wechseln", "H-KW 204");
Auftrag a2 = new Auftrag("Ölwechsel", "H-LT 88");
Auftrag a3 = new Auftrag("Reifen einlagern", "HI-A 512");
a1.zuweisen(m);
a2.zuweisen(m);
a1.abschliessen();
a1.abschliessen();
a3.abschliessen();
Ansatz: Jede der drei Methoden beginnt mit einer Prüfung von mechaniker auf null. Erst danach darfst du Methoden über die Referenz aufrufen.
Delegation: Der Auftrag ändert den Zähler des Mechanikers nicht selbst (der ist privat), sondern ruft m.auftragUebernehmen() bzw. mechaniker.auftragAbschliessen() auf.
Ablauf: Zwei Zuweisungen → Zähler 2. Wie oft wird danach wirklich abgeschlossen?
Vollständige Lösung
public class Auftrag {
    private String beschreibung;
    private String kennzeichen;
    private Mechaniker mechaniker;
    private boolean erledigt;

    public Auftrag(String beschreibung, String kennzeichen) {
        this.beschreibung = beschreibung;
        this.kennzeichen = kennzeichen;
        mechaniker = null;
        erledigt = false;
    }

    public boolean zuweisen(Mechaniker m) {
        if (mechaniker == null && m != null) {
            mechaniker = m;
            m.auftragUebernehmen();
            return true;
        }
        return false;
    }

    public String getMechanikerName() {
        if (mechaniker == null) {
            return "nicht zugewiesen";
        }
        return mechaniker.getName();
    }

    public void abschliessen() {
        if (mechaniker != null && !erledigt) {
            erledigt = true;
            mechaniker.auftragAbschliessen();
        }
    }
}

Ablauf: Nach zwei Zuweisungen hat Deniz 2 offene Aufträge. Der erste a1.abschliessen() senkt auf 1. Der zweite Aufruf ändert nichts, weil a1 schon erledigt ist. a3.abschliessen() ändert ebenfalls nichts: a3 hat keinen Mechaniker — ohne die Null-Prüfung gäbe es hier eine NullPointerException. Ergebnis: 1.

A8
Zeichenketten zerlegen: Konzerttickets
AFB II

Ein Konzertveranstalter codiert Tickets als Zeichenkette aus Bereich und Platznummer, getrennt durch einen Bindestrich, z. B. "Innenraum-112" oder "Balkon-7".

Konzert
  • - verkauft: Reihung vom Typ Ticket
  • - anzahl: Ganzzahl
  • c Konzert(kapazitaet: Ganzzahl)
  • + verkaufen(t: Ticket): Wahrheitswert
  • + anzahlImBereich(bereich: Zeichenkette): Ganzzahl
hat verkauft1n
Ticket
  • - code: Zeichenkette
  • c Ticket(code: Zeichenkette)
  • + getBereich(): Zeichenkette
  • + getPlatz(): Zeichenkette

a) Ermitteln Sie für code = "Innenraum-112" die Rückgaben von code.length() und code.indexOf("-").

b) Implementieren Sie getBereich() und getPlatz() in Ticket sowie anzahlImBereich(bereich) in Konzert.

Ansatz a: „Innenraum“ hat 9 Buchstaben (Index 0 bis 8). Der Bindestrich folgt direkt dahinter.
Teilstrings: Der Bereich reicht von 0 bis ausschließlich zum Bindestrich: substring(0, code.indexOf("-")). Der Platz beginnt ein Zeichen hinter dem Bindestrich und reicht bis code.length().
Zählen: In anzahlImBereich läuft eine Schleife bis anzahl und vergleicht mit equals — nicht mit ==.
Vollständige Lösung

a) code.length() = 13 (9 + 1 + 3 Zeichen), code.indexOf("-") = 9.

b)

public class Ticket {
    private String code;

    public Ticket(String code) {
        this.code = code;
    }

    public String getBereich() {
        return code.substring(0, code.indexOf("-"));
    }

    public String getPlatz() {
        return code.substring(code.indexOf("-") + 1, code.length());
    }
}
public int anzahlImBereich(String bereich) {
    int zaehler = 0;
    for (int i = 0; i < anzahl; i++) {
        if (verkauft[i].getBereich().equals(bereich)) {
            zaehler = zaehler + 1;
        }
    }
    return zaehler;
}

Weil die Methode getBereich() das Zerlegen übernimmt, muss Konzert das Format der Codes nicht kennen. Ändert sich das Format, wird nur Ticket angepasst.

A9
Objektdiagramm zeichnen: Tierarztpraxis
AFB II

Die Software einer Tierarztpraxis speichert zu jedem Tier seinen Halter.

Tier
  • - name: Zeichenkette
  • - gewichtKg: Fließkommazahl
  • - halter: Halter
  • c Tier(name: Zeichenkette, gewichtKg: Fließkommazahl, halter: Halter)
  • + setHalter(halter: Halter)
  • + zunehmen(kg: Fließkommazahl)
  • + getGewichtKg(): Fließkommazahl
gehört zun1
Halter
  • - name: Zeichenkette
  • - telefon: Zeichenkette
  • c Halter(name: Zeichenkette, telefon: Zeichenkette)
Halter h1 = new Halter("Frau Yilmaz", "0511 4471");
Tier t1 = new Tier("Bruno", 31.5, h1);
Tier t2 = new Tier("Minka", 4.25, h1);
Halter h2 = new Halter("Herr Brandt", "0511 9020");
t2.setHalter(h2);
t1.zunehmen(0.75);
Tier t3 = t1;
t3.zunehmen(0.5);

Zeichnen Sie das Objektdiagramm nach der letzten Anweisung (alle Objekte mit Attributwerten und Referenzen) und geben Sie das Gewicht von Bruno an.

kg
Ansatz: Zähle zuerst die new-Aufrufe: So viele Objektkarten zeichnest du. t3 ist nur ein weiterer Name.
Referenzen: Das Attribut halter enthält kein Halter-Objekt, sondern einen Verweis — zeichne einen Pfeil oder schreibe halter = h1.
Gewicht: t1 und t3 verweisen auf dasselbe Tier: 31,5 + 0,75 + 0,5.
Vollständige Lösung
t1: Tier
  • name = "Bruno"
  • gewichtKg = 32.75
  • halter = h1
t2: Tier
  • name = "Minka"
  • gewichtKg = 4.25
  • halter = h2
h1: Halter
  • name = "Frau Yilmaz"
  • telefon = "0511 4471"
h2: Halter
  • name = "Herr Brandt"
  • telefon = "0511 9020"

Es gibt vier Objekte. t3 ist ein Alias von t1 (keine eigene Karte, höchstens ein zweiter Pfeil auf Bruno). Minka verweist nach setHalter(h2) auf Herrn Brandt; Frau Yilmaz wird weiterhin von Bruno referenziert. Bruno wiegt 31,5 + 0,75 + 0,5 = 32,75 kg.

Die Halter-Objekte kennen ihre Tiere nicht — die Assoziation ist hier nur in einer Richtung umgesetzt.

A10
Setter mit Prüfung: Stromzähler
AFB II

Ein Stromzähler läuft nie rückwärts, und der Tarif muss zwischen 20 und 60 Cent pro kWh liegen. Ungültige Werte werden ignoriert; ablesen liefert dann 0.

public class Stromzaehler {
    private int zaehlerstand;
    private int tarifCent;

    public Stromzaehler(int start) {
        zaehlerstand = start;
        tarifCent = 30;
    }

    public void setTarifCent(int tarifCent) {
        if (tarifCent >= 20 && tarifCent <= 60) {
            this.tarifCent = tarifCent;
        }
    }

    public int ablesen(int neuerStand) {
        if (neuerStand < zaehlerstand) {
            return 0;
        }
        int verbrauch = neuerStand - zaehlerstand;
        zaehlerstand = neuerStand;
        return verbrauch;
    }

    public int getZaehlerstand() {
        return zaehlerstand;
    }

    public int getTarifCent() {
        return tarifCent;
    }
}
Stromzaehler z = new Stromzaehler(10250);
z.setTarifCent(35);
z.setTarifCent(5);
int v1 = z.ablesen(10430);
int v2 = z.ablesen(10400);
int v3 = z.ablesen(10510);
int kosten = (v1 + v2 + v3) * z.getTarifCent();

Bestimmen Sie den Zählerstand und den Wert von kosten am Ende. Begründen Sie, warum die Klasse keinen öffentlichen Setter setZaehlerstand anbietet.

kWh
Cent
Ansatz: Gehe Zeile für Zeile durch und notiere zaehlerstand, tarifCent und die Rückgaben v1, v2, v3.
Abgelehnt: 5 Cent liegt außerhalb von 20–60, der Tarif bleibt 35. 10400 ist kleiner als der aktuelle Stand 10430 — keine Änderung, Rückgabe 0.
Vollständige Lösung

Tarif: 30 → 35 (5 wird abgelehnt). v1 = 10430 − 10250 = 180; v2 = 0 (10400 < 10430, abgelehnt); v3 = 10510 − 10430 = 80. Zählerstand am Ende: 10510 kWh. kosten = (180 + 0 + 80) · 35 = 9100 Cent (91 €).

Begründung: Ein freier Setter würde erlauben, den Zählerstand zurückzudrehen — der Zustand wäre dann nicht mehr gültig, und Verbräuche könnten verschwinden. Stattdessen gibt es die fachliche Methode ablesen, die nur plausible Werte annimmt und zugleich den Verbrauch liefert. Das ist der Kern der Datenkapselung: Das Objekt selbst sorgt dafür, dass sein Zustand gültig bleibt.

z: Stromzaehler
  • zaehlerstand = 10250
  • tarifCent = 30
Objektkarte direkt nach dem Konstruktor