MINT lernen

Übungen: Objekte, die Objekte kennen

Ein Objekt allein kann wenig — erst wenn es andere Objekte kennt, entstehen Ketten wie k.getFlug().getZiel().

Dein Fortschritt:
0 / 0 Aufgaben
1

Übungsaufgaben

Zehn Übungen zum Klicken, Ziehen und Knobeln — von AFB I bis AFB III. Jede Übung gibt dir sofort Rückmeldung; wenn du hängst, helfen dir die gestuften Tipps.

A1
Kurse im Fitnessstudio
AFB I

Ein Fitnessstudio plant seine Kurse mit diesem Klassendiagramm. Welche Aussagen stimmen?

Kurs
  • - titel: Zeichenkette
  • - plaetze: Ganzzahl
  • - leitung: Trainerin
  • c Kurs(titel: Zeichenkette, leitung: Trainerin)
  • + getLeitung(): Trainerin
  • + setLeitung(t: Trainerin)
  • + getTitel(): Zeichenkette
wird geleitet vonn1
Trainerin
  • - name: Zeichenkette
  • - telefon: Zeichenkette
  • c Trainerin(name: Zeichenkette)
  • + getName(): Zeichenkette
Mehrere Antworten sind richtig. Markiere alle zutreffenden und klicke dann auf „Prüfen“.
Die Beziehung steckt im Attribut leitung der Klasse Kurs — in Trainerin gibt es kein Attribut vom Typ Kurs, also kennt die Trainerin ihre Kurse nicht. Die Kardinalitäten lesen sich so: Ein Kurs wird von genau einer Trainerin geleitet, eine Trainerin kann n Kurse leiten. Gespeichert wird nur ein Verweis, keine Kopie: Ändert die Trainerin ihre Telefonnummer, sehen das alle Kurse, die sie kennen. Typischer Fehler: getLeitung() mit dem Namen verwechseln — sie liefert das ganze Objekt; den Namen bekommt man erst mit k.getLeitung().getName().
Ansatz: Suche das Attribut, dessen Typ eine andere Klasse aus dem Diagramm ist.
Weiter: Wer ein Attribut vom Typ der anderen Klasse hat, kennt die andere. Achte auf den Rückgabetyp von getLeitung.
A2
Wer spielt welche Rolle? — Die Theater-AG
AFB I

Die Theater-AG verwaltet ihre Aufführung mit einer Klasse Rolle. Es gibt außerdem die Klassen Person und Kostuem. Ordne die Einträge der Klassenkarte zu.

Ziehe jede Karte in den passenden Korb — oder wähle sie mit Enter aus und drücke dann die Ziffer des Korbs (0 legt sie zurück).
1Attribut mit Standard-Datentyp
2Attribut für eine Beziehung
3Methode
Ein Attribut drückt eine Beziehung aus, wenn sein Typ eine eigene Klasse des Modells ist — hier Person oder Kostuem. Eine Zeichenkette wie name gehört zu den Standard-Datentypen, auch wenn String in Java eine Klasse ist. Typischer Fehler: getDarsteller(): Person zu den Beziehungen legen — die Methode nutzt die Beziehung, gespeichert wird sie aber im Attribut. Übrigens: Zwei Attribute vom Typ Person heißt, eine Rolle kann zwei Personen kennen.
Ansatz: Klammern hinter dem Namen verraten eine Methode. Bei den Attributen entscheidet der Typ hinter dem Doppelpunkt.
Weiter: Ganzzahl, Wahrheitswert und Zeichenkette sind Standard-Datentypen. Person und Kostuem sind Klassen aus dem Modell.
A3
Ein Termin kennt seinen Raum
AFB I

Im Schulkalender kennt jeder Termin den Raum, in dem er stattfindet. Die Klasse Raum hat den Konstruktor Raum(nummer: Zeichenkette) und die Methode getNummer(). Setze ein — ein Wort bleibt übrig.

Termin
  • - titel: Zeichenkette
  • - ort: Raum
  • c Termin(titel: Zeichenkette, ort: Raum)
  • + getOrt(): Raum
  • + setOrt(r: Raum)
findet statt inn0..1
Raum
  • - nummer: Zeichenkette
  • c Raum(nummer: Zeichenkette)
  • + getNummer(): Zeichenkette
Wort anklicken, dann Lücke anklicken (oder umgekehrt) — mit Tab und Enter geht es genauso. Ein Klick auf eine gefüllte Lücke legt das Wort zurück.

In Java wird die Beziehung zum Attribut private ort;. Im Konstruktor stellt .ort = ort; die Beziehung her. Den Raum muss es vorher schon geben — ein Raum-Objekt entsteht nur mit . Hat ein Termin noch keinen Raum (Kardinalität 0..1), dann ist ort gleich . Die Raumnummer eines Termins t erfährt man mit t.().getNummer().

String bleibt übrig — und genau das ist der typische Fehler: Wer private String ort; schreibt, speichert nur einen Text. Der Termin kennt dann kein Raum-Objekt und kann dessen Methoden nicht nutzen. Die Kardinalität 0..1 heißt: Ein Termin kennt höchstens einen Raum, darf aber auch (noch) keinen kennen — dann steht null im Attribut.
Ansatz: Der Typ des Attributs ist die Klasse, die gekannt wird.
Weiter: Auf ein bekanntes Objekt greift man über den Getter zu und ruft dann dessen Methode auf: t.getOrt().getNummer().
A4
Stimmt's? — Handy und Tarif
AFB II

Ein Mobilfunkanbieter modelliert: Jedes Smartphone kennt seinen Tarif (Name, Datenvolumen in GB, Getter und setVolumen).

Tarif basis = new Tarif("Basic", 5);
Tarif max = new Tarif("Max", 40);
Smartphone s1 = new Smartphone("Lina", basis);
Smartphone s2 = new Smartphone("Tom", basis);
Smartphone s3 = new Smartphone("Eda", null);
Fünf Aussagen nacheinander. Eine falsche Einschätzung reicht — dann startest du die Serie mit „Neue Runde“ neu.
Aussage 1 von 5

Bei einer Beziehung speichert jedes Objekt nur einen Verweis. Mehrere Objekte können dasselbe Objekt kennen — eine Änderung daran sehen dann alle. Vor einer Delegationskette wie s3.getTarif().getName() muss man sicher sein, dass kein Glied null ist.
Ansatz: Zeichne die drei Smartphones und die zwei Tarife als Kästchen und verbinde sie mit Pfeilen.
Weiter: Die Tarif-Objekte existieren schon, bevor die Smartphones erzeugt werden. Eine Kette a.getB().getC() bricht ab, wenn a.getB() null liefert.
A5
Koffer am Flughafen
AFB II

Beim Einchecken wird jeder Koffer einem Flug zugeordnet (Kardinalität: ein Koffer kennt genau einen Flug). Die Klasse Flug hat den Konstruktor Flug(nummer, ziel) und die Methode getZiel(). Verbinde jede Aufgabe mit dem passenden Java-Baustein.

Ansatz: Beginne mit den eindeutigen Zuordnungen: Welche Zeile ist ein Attribut, welche ein Konstruktor?
Weiter: Ein Setter stellt eine Beziehung neu her, ein Getter liefert das gekannte Objekt. Eine Zeile mit zwei Punkten fragt über das gekannte Objekt nach.
A6
Zwei E-Scooter, ein Akku
AFB IIMix

Ein E-Scooter kennt seinen Wechselakku. Beim Fahren wird der Akku in einer Schleife (Kapitel 1) entladen, bis er zu schwach ist.

public class Akku {
    private int ladung;

    public Akku(int ladung) {
        this.ladung = ladung;
    }

    public int getLadung() {
        return ladung;
    }

    public void entladen(int prozent) {
        ladung = ladung - prozent;
    }
}
public class Scooter {
    private Akku akku;
    private int km;

    public Scooter(Akku akku) {
        this.akku = akku;
        km = 0;
    }

    public void fahren() {
        while (akku.getLadung() >= 15) {
            akku.entladen(15);
            km = km + 2;
        }
    }

    public int getKm() {
        return km;
    }
}
Akku a = new Akku(80);
Scooter s = new Scooter(a);
s.fahren();
Scooter t = new Scooter(a);
t.fahren();

Welchen Wert hat s.getKm() + t.getKm()?

Rechne selbst und trage das Ergebnis ein — Enter prüft direkt.
Die Schleife läuft bei s fünfmal: 80 → 65 → 50 → 35 → 20 → 5. Bei 5 % ist die Bedingung 5 >= 15 falsch, also s.getKm() = 5 · 2 = 10. Der zweite Scooter t bekommt denselben Akku a — mit nur noch 5 %. Seine Schleife läuft kein einziges Mal: t.getKm() = 0. Summe 10. Häufige Fehler: 20 (als bekäme t einen frisch geladenen Akku) oder 12 (die Schleife einmal zu oft, bei 5 % noch gefahren).
Ansatz: Verfolge die Ladung Durchlauf für Durchlauf. Wann ist die Bedingung der while-Schleife zum ersten Mal falsch?
Weiter: Beide Scooter kennen dasselbe Akku-Objekt. Welche Ladung hat es, wenn t losfahren will?
A7
Staffellauf beim Sportfest
AFB II

Beim Staffellauf kennt jede Läuferin bzw. jeder Läufer den Nachfolger, dem der Stab übergeben wird (0..1 — der letzte hat keinen). Die Klasse Laeufer hat den Konstruktor Laeufer(name, nachfolger) und die Methoden getName() und getNachfolger(). Bringe die Zeilen in die einzige Reihenfolge, in der das Programm übersetzt wird.

Ziehe die Karten in die richtige Reihenfolge — mit der Tastatur: ↑/↓ verschiebt, Shift+↑/↓ wechselt nur den Fokus.
1Laeufer d = new Laeufer("Dana", null);
2Laeufer c = new Laeufer("Cem", d);
3Laeufer b = new Laeufer("Ben", c);
4Laeufer a = new Laeufer("Ada", b);
5String name = a.getNachfolger().getNachfolger().getName();
Wenn die Beziehung im Konstruktor hergestellt wird, muss das gekannte Objekt vorher existieren. Deshalb baut man die Kette von hinten auf: erst Dana (kennt niemanden, also null), dann Cem, Ben und zuletzt Ada. Die letzte Zeile folgt zweimal der Beziehung: Ada → Ben → Cem, also ist name gleich "Cem". Typischer Fehler: mit Ada anfangen, weil sie zuerst läuft — dann ist b noch gar nicht deklariert.
Ansatz: Welche Variable wird in welcher Zeile benutzt? Eine Variable muss deklariert sein, bevor man sie übergibt.
Weiter: Nur Dana braucht kein anderes Objekt — sie steht ganz oben.
A8
Loks und Züge
AFB III

Ein Eisenbahnverkehrsunternehmen ordnet seinen Zügen Loks zu. Wie viele Wagen ein Zug ziehen darf, hängt von der Leistung seiner Lok ab.

public class Lok {
    private String name;
    private int leistung;

    public Lok(String name, int leistung) {
        this.name = name;
        this.leistung = leistung;
    }

    public int getLeistung() {
        return leistung;
    }

    public void drosseln(int kw) {
        leistung = leistung - kw;
    }
}
public class Zug {
    private String linie;
    private Lok lok;

    public Zug(String linie, Lok lok) {
        this.linie = linie;
        this.lok = lok;
    }

    public void setLok(Lok lok) {
        this.lok = lok;
    }

    public Lok getLok() {
        return lok;
    }

    public int maxWagen() {
        if (lok == null) {
            return 0;
        }
        return lok.getLeistung() / 500;
    }
}
Lok l1 = new Lok("E-103", 6000);
Lok l2 = new Lok("V-200", 2000);
Zug z1 = new Zug("IC 41", l1);
Zug z2 = new Zug("RE 7", l1);
Zug z3 = new Zug("RB 12", null);
z2.setLok(l2);
z3.setLok(z1.getLok());
l1.drosseln(1000);
Rechne die Kette Schritt für Schritt: Erst wenn ein Schritt stimmt, wird der nächste freigeschaltet. Enter prüft.
  1. Rückgabewert von z3.maxWagen() direkt nach dem Erzeugen von z3 Wagen
  2. Rückgabewert von z2.maxWagen() am Ende Wagen
  3. Rückgabewert von z3.maxWagen() am Ende Wagen
  4. Summe von maxWagen() über alle drei Züge am Ende Wagen
Solange z3 keine Lok kennt, fängt die null-Prüfung den Aufruf ab: 0. z2 bekommt die Lok l2: 2000 / 500 = 4. z3.setLok(z1.getLok()) übergibt keine Kopie, sondern einen Verweis auf dieselbe Lok l1. Das Drosseln wirkt daher auf z1 und z3: je 5000 / 500 = 10. Summe 10 + 4 + 10 = 24. Typische Fehler: 12 für z3 (als hätte z3 eine eigene Lok mit der alten Leistung) oder 36 (übersehen, dass z2 umgestellt wurde).
Ansatz: Zeichne die zwei Loks und die drei Züge und ziehe Pfeile von den Zügen zu ihren Loks. Aktualisiere die Pfeile bei jedem setLok.
Weiter: Nach allen Zeilen: z1 → l1, z2 → l2, z3 → l1. l1 hat nur noch 5000 kW.
A9
Fehlersuche: Patient und Hausarzt
AFB III

Eine Praxissoftware soll speichern, welchen Hausarzt ein Patient hat (0..1). Neue Patienten haben zunächst keinen Hausarzt. Die Klasse Arzt ist fertig:

Arzt
  • - name: Zeichenkette
  • c Arzt(name: Zeichenkette)
  • + getName(): Zeichenkette

In der Klasse Patient stecken drei Fehler — zwei Übersetzungsfehler und einer, der zu falschem Verhalten führt.

In diesem Text stecken Fehler. Klicke genau die falschen Zeilen an — die richtigen musst du stehen lassen.
Die null-Prüfung in getArztName ist richtig und wichtig: Ohne sie gäbe es bei Patienten ohne Hausarzt eine NullPointerException. Der Fehler in setHausarzt ist der tückischste — das Programm läuft, aber jeder Patient meldet für immer „kein Hausarzt“.
Ansatz: Prüfe drei Stellen: Womit wird hausarzt anfangs belegt? Wird die Beziehung im Setter wirklich gespeichert? Wie greift Patient auf den Namen des Arztes zu?
Weiter: Parameter und Attribut heißen im Setter gleich. Und private Attribute einer anderen Klasse sind nur über deren Methoden erreichbar.
A10
Trickaufgabe: Geocaching im Kreis
AFB IIITrick

Eine Geocaching-Runde besteht aus Stationen; jede Station kennt die nächste. Nach der letzten geht es wieder zur ersten.

public class Station {
    private int nummer;
    private int punkte;
    private Station weiter;

    public Station(int nummer, int punkte) {
        this.nummer = nummer;
        this.punkte = punkte;
        weiter = null;
    }

    public void setWeiter(Station weiter) {
        this.weiter = weiter;
    }

    public Station getWeiter() {
        return weiter;
    }

    public int getNummer() {
        return nummer;
    }

    public int getPunkte() {
        return punkte;
    }
}
Station s1 = new Station(1, 2);
Station s2 = new Station(2, 3);
Station s3 = new Station(3, 1);
s1.setWeiter(s2);
s2.setWeiter(s3);
s3.setWeiter(s1);
s2 = s3;

Welchen Wert liefert jeder Ausdruck nach diesen Zeilen?

Wähle für jede Zeile eine Stufe: 0 = Wert 0, 1 = Wert 1, 2 = Wert 2, 3 = Wert 3, 4 = Wert 4. Mit der Tastatur: Tab zur Zeile, ←/→ zwischen den Stufen, Enter setzt.
0 = Wert 04 = Wert 4
s1.getWeiter().getNummer()
s2.getWeiter().getNummer()
s1.getWeiter().getWeiter().getWeiter().getNummer()
s3.getWeiter().getWeiter().getPunkte()
s1.getWeiter().getPunkte() + s2.getPunkte()
Der Trick steckt in der letzten Zeile: s2 = s3; ändert nur die Variable s2 — sie zeigt jetzt auf Station 3. Die Beziehungen zwischen den Objekten bleiben unverändert: Station 1 kennt weiterhin Station 2, obwohl keine Variable mehr auf Station 2 zeigt. Also liefert s1.getWeiter() immer noch Station 2 (Nummer 2, 3 Punkte), während s2.getWeiter() jetzt Station 1 ist. Dreimal weiter führt einmal im Kreis zurück zu Station 1. Wer bei Zeile 2 den Wert 3 gewählt hat, hat s2 noch als Station 2 gelesen.
Ansatz: Zeichne die drei Stationen als Kreis mit Pfeilen 1 → 2 → 3 → 1. Setze die Variablen s1, s2, s3 als Schildchen an die Stationen.
Weiter: s2 = s3 versetzt nur das Schildchen s2 an Station 3 — die Pfeile zwischen den Stationen ändern sich nicht.