MINT lernen

Übung — AFB I (Reproduzieren)

Zehn Grundaufgaben quer durch das Kapitel: Klassenkarten lesen, Zustände verfolgen, Getter und Setter schreiben, Zeichenketten auswerten.

Dein Fortschritt:
0 / 0 Aufgaben
1

Aufgabenblock — AFB I

Zehn Standardaufgaben quer durch das ganze Kapitel — bunt gemischt statt nach Unterkapiteln sortiert: Klassenkarten und Objektkarten lesen, Zustände nach Methodenaufrufen angeben, einfache Klassen notieren, Referenzen, Reihungen und Zeichenketten-Methoden. Das sind die sicheren Punkte in jeder Klausur zur Objektorientierung.

A1
Klassenkarte lesen: Song
AFB I

Eine Playlist-App verwendet die folgende Klasse. Entnehmen Sie der Klassenkarte, wie viele Parameter der Konstruktor hat und wie viele der Methoden Anfragen bzw. Aufträge sind.

Song
  • - titel: Zeichenkette
  • - interpret: Zeichenkette
  • - dauerSek: Ganzzahl
  • - favorit: Wahrheitswert
  • c Song(titel: Zeichenkette, interpret: Zeichenkette, dauerSek: Ganzzahl)
  • + getTitel(): Zeichenkette
  • + getDauerSek(): Ganzzahl
  • + istFavorit(): Wahrheitswert
  • + markieren()
  • + setDauerSek(dauerSek: Ganzzahl)
Ansatz: Parameter stehen im Konstruktor in der Klammer, jeweils als name: Typ, durch Kommas getrennt.
Unterscheidung: Eine Anfrage liefert einen Wert zurück — in der Karte steht dann hinter der Klammer ein Rückgabetyp. Ein Auftrag ändert den Zustand und hat keinen Rückgabetyp.
Kontrolle: Der Konstruktor ist weder Anfrage noch Auftrag. Zähle also nur die fünf Zeilen mit +.
Vollständige Lösung

Der Konstruktor c Song(titel, interpret, dauerSek) hat 3 Parameter. favorit wird nicht übergeben, sondern im Konstruktor auf false gesetzt.

Anfragen (3): getTitel(): Zeichenkette, getDauerSek(): Ganzzahl, istFavorit(): Wahrheitswert — sie haben einen Rückgabetyp.
Aufträge (2): markieren() und setDauerSek(dauerSek: Ganzzahl) — kein Rückgabetyp, sie ändern den Zustand.

Beachte: Für interpret gibt es keinen Getter. Das Attribut ist privat und damit von außen weder lesbar noch änderbar — ob das so gewollt ist, müsste man im Entwurf klären.

A2
Zustand nach Methodenaufrufen: Kaffeeautomat
AFB I

Die Methode kaffeeZubereiten() verbraucht 14 g Bohnen und erhöht becher um 1 — aber nur, wenn noch mindestens 14 g vorhanden sind. auffuellen(gramm) erhöht den Bohnenvorrat.

Kaffeeautomat k = new Kaffeeautomat(40);
k.kaffeeZubereiten();
k.kaffeeZubereiten();
k.kaffeeZubereiten();
k.auffuellen(30);
k.kaffeeZubereiten();

Geben Sie die Attributwerte des Objekts k nach allen Aufrufen an.

g
Ansatz: Lege eine kleine Tabelle an: Zeile für Zeile den Wert von bohnenGramm und becher fortschreiben.
Stolperstelle: Nach zwei Kaffees sind noch 12 g übrig. Was passiert beim dritten Aufruf, wenn die Bedingung bohnenGramm >= 14 nicht erfüllt ist?
Vollständige Lösung

Die Methode im Quelltext:

public class Kaffeeautomat {
    private int bohnenGramm;
    private int becher;

    public Kaffeeautomat(int bohnenGramm) {
        this.bohnenGramm = bohnenGramm;
        becher = 0;
    }

    public void kaffeeZubereiten() {
        if (bohnenGramm >= 14) {
            bohnenGramm = bohnenGramm - 14;
            becher = becher + 1;
        }
    }

    public void auffuellen(int gramm) {
        bohnenGramm = bohnenGramm + gramm;
    }

    public int getBohnenGramm() {
        return bohnenGramm;
    }

    public int getBecher() {
        return becher;
    }
}

Ablauf: 40 g / 0 → 26 g / 1 → 12 g / 2 → dritter Aufruf: 12 < 14, nichts passiert → auffüllen: 42 g / 2 → 28 g / 3.

k: Kaffeeautomat
  • bohnenGramm = 28
  • becher = 3

Typischer Fehler: 4 Becher und 0 g — dann wurde die Bedingung im dritten Aufruf übersehen.

k: Kaffeeautomat
  • bohnenGramm = 40
  • becher = 0
Objektkarte direkt nach dem Konstruktor
A3
Zeichenketten-Methoden: Haltestellenanzeige
AFB I

Die Anzeige im Schulbus speichert den Namen der nächsten Haltestelle: String h = "Am Rathausplatz";

Nennen Sie die Rückgaben von h.length() und h.indexOf("platz"). Nennen Sie außerdem die Rückgaben von h.substring(3, 10) und h.charAt(h.length() - 1).

Ansatz: Schreibe die Zeichen mit ihrem Index darunter. Der Index beginnt bei 0, und das Leerzeichen zählt mit.
Regeln: indexOf liefert den Index des ersten Zeichens des gefundenen Teilstrings. substring(a, b) liefert die Zeichen von Index a bis ausschließlich b.
Kontrolle: Das letzte Zeichen hat immer den Index length() − 1, nie length().
Vollständige Lösung

Indizes: A0 m1 ␣2 R3 a4 t5 h6 a7 u8 s9 p10 l11 a12 t13 z14

h.length() → 15 (Indizes 0 bis 14).
h.indexOf("platz") → 10 („p“ steht an Index 10).
h.substring(3, 10) → "Rathaus" (Index 3 bis 9; Index 10 gehört nicht mehr dazu).
h.charAt(h.length() - 1) → das Zeichen 'z' (Index 14). h.charAt(h.length()) würde dagegen einen Laufzeitfehler auslösen.

A4
Konstruktor, Getter und Setter: Pflanze
AFB I

Ein Gewächshaus-Programm verwaltet Pflanzen. Die Höhe soll nur Werte von 0 bis 500 cm annehmen; ungültige Werte werden ignoriert. Nach dem Erzeugen ist die Höhe 0.

Pflanze
  • - art: Zeichenkette
  • - hoeheCm: Ganzzahl
  • c Pflanze(art: Zeichenkette)
  • + getHoeheCm(): Ganzzahl
  • + setHoeheCm(hoeheCm: Ganzzahl)

Stellen Sie die Klasse vollständig als Java-Quelltext dar.

Ansatz: Reihenfolge im Klassenrumpf: Attribute (private), Konstruktor (ohne Rückgabetyp, auch ohne void), dann die Methoden.
Setter: Der Parameter heißt genauso wie das Attribut. Ohne this. würdest du nur den Parameter mit sich selbst überschreiben. Die Zuweisung steht in einem if mit der Bereichsprüfung.
Vollständige Lösung
public class Pflanze {
    private String art;
    private int hoeheCm;

    public Pflanze(String art) {
        this.art = art;
        hoeheCm = 0;
    }

    public int getHoeheCm() {
        return hoeheCm;
    }

    public void setHoeheCm(int hoeheCm) {
        if (hoeheCm >= 0 && hoeheCm <= 500) {
            this.hoeheCm = hoeheCm;
        }
    }
}

Häufige Fehler: public void Pflanze(...) ist kein Konstruktor, sondern eine Methode; hoeheCm = hoeheCm; ohne this ändert das Attribut nicht; ein Getter ohne return kompiliert nicht.

A5
Vom Quelltext zur Klassenkarte: Handyvertrag
AFB I

Gegeben ist der Quelltext einer Klasse aus der Kundenverwaltung eines Mobilfunkanbieters.

public class Handyvertrag {
    private String nummer;
    private int datenvolumenMB;
    private int verbrauchtMB;
    private boolean gesperrt;

    public Handyvertrag(String nummer, int datenvolumenMB) {
        this.nummer = nummer;
        this.datenvolumenMB = datenvolumenMB;
        verbrauchtMB = 0;
        gesperrt = false;
    }

    public int getRestMB() {
        return datenvolumenMB - verbrauchtMB;
    }

    public void surfen(int mb) {
        if (!gesperrt) {
            verbrauchtMB = verbrauchtMB + mb;
            pruefen();
        }
    }

    public boolean istGesperrt() {
        return gesperrt;
    }

    private void pruefen() {
        if (verbrauchtMB >= datenvolumenMB) {
            gesperrt = true;
        }
    }
}

Zeichnen Sie die zugehörige Klassenkarte in der Notation der Ergänzenden Hinweise.

Ansatz: Drei Felder: Name — Attribute — Konstruktor und Methoden. private wird zu -, public zu +.
Typen übersetzen: int → Ganzzahl, String → Zeichenkette, boolean → Wahrheitswert. Der Konstruktor bekommt ein vorangestelltes c; void wird einfach weggelassen.
Achtung: Es gibt eine private Methode — auch sie gehört in die Karte. Und getRestMB() hat kein gleichnamiges Attribut: Der Wert wird berechnet.
Vollständige Lösung
Handyvertrag
  • - nummer: Zeichenkette
  • - datenvolumenMB: Ganzzahl
  • - verbrauchtMB: Ganzzahl
  • - gesperrt: Wahrheitswert
  • c Handyvertrag(nummer: Zeichenkette, datenvolumenMB: Ganzzahl)
  • + getRestMB(): Ganzzahl
  • + surfen(mb: Ganzzahl)
  • + istGesperrt(): Wahrheitswert
  • - pruefen()

Nur die Attribute kommen ins mittlere Feld — lokale Variablen oder berechnete Werte wie der Rest nicht. pruefen() ist eine private Hilfsmethode und wird mit - notiert.

A6
Referenz oder Wert? Fitnesstracker
AFB I

Die Klasse Tracker hat ein Attribut schritte (Start 0), eine Methode schritteAddieren(anzahl) und den Getter getSchritte().

Tracker a = new Tracker("Mia");
a.schritteAddieren(4200);
Tracker b = a;
b.schritteAddieren(800);
int s = a.getSchritte();
s = s + 1000;
Tracker c = new Tracker("Mia");
c.schritteAddieren(s);

Bestimmen Sie die Rückgaben von a.getSchritte() und c.getSchritte() nach dem letzten Befehl sowie die Anzahl der erzeugten Tracker-Objekte.

Ansatz: Zähle die new. Jede Variable vom Typ Tracker ist nur ein Verweis auf ein Objekt.
Alias: Nach b = a zeigen beide Variablen auf dasselbe Objekt. s ist dagegen eine int-Variable: Sie enthält eine Kopie des Werts.
Vollständige Lösung

Es gibt 2 Objekte (zweimal new). a und b verweisen auf dasselbe Objekt: 4200 + 800 = 5000. s erhält eine Kopie dieses Werts; s = s + 1000; ändert nur s (6000), nicht das Objekt. Das zweite Objekt c hat trotz gleichem Namen „Mia“ eine eigene Identität und bekommt 6000 Schritte.

a: Tracker
  • name = "Mia"
  • schritte = 5000
c: Tracker
  • name = "Mia"
  • schritte = 6000

Die linke Karte ist das Objekt, auf das a und b verweisen.

A7
Phasen im Softwareprojekt: Klassenfahrt-App
AFB I

Ein Kurs entwickelt im Team eine App, mit der sich Teilnehmende einer Klassenfahrt in Zimmer eintragen. Ordnen Sie jede Tätigkeit einer Projektphase (Analyse, Entwurf, Implementierung, Test) zu.

  1. Mit der Lehrkraft wird geklärt, dass ein Zimmer höchstens vier Betten hat und Jungen und Mädchen getrennt wohnen.
  2. Das Team legt fest: Die Klasse Zimmer bekommt die Methode + belegen(t: Teilnehmer): Wahrheitswert.
  3. Es wird notiert: „Ein fünfter Teilnehmer in einem vollen Viererzimmer → belegen liefert false.“
  4. Jonas schreibt die Methode belegen in Java.
  5. Die Anforderungen werden in einem Pflichtenheft zusammengefasst.
  6. Das Klassendiagramm wird im Team abgestimmt, bevor jemand programmiert.
Ansatz: Frage dich bei jeder Tätigkeit: Geht es um was das Programm können soll (Analyse), wie es aufgebaut ist (Entwurf), um Quelltext (Implementierung) oder um das Prüfen (Test)?
Stolperstelle c: Testfälle werden sinnvollerweise schon vor dem Programmieren formuliert — sie gehören trotzdem zur Testphase.
Vollständige Lösung

a) Analyse — fachliche Anforderung klären. b) Entwurf — Methodensignatur festlegen. c) Test — Testfall mit erwartetem Ergebnis. d) Implementierung. e) Analyse — das Pflichtenheft ist ihr Ergebnis. f) Entwurf — das abgestimmte Klassendiagramm ist die Schnittstelle, an die sich alle beim Programmieren halten.

A8
Reihung von Objekten: Aquarium
AFB I

Ein Aquariumprogramm speichert die Fische eines Beckens in einer Reihung.

Fisch[] becken = new Fisch[6];
becken[0] = new Fisch("Guppy", 4);
becken[2] = new Fisch("Neon", 3);
becken[5] = becken[0];
Fisch f = new Fisch("Wels", 12);

Geben Sie an, wie viele Fisch-Objekte nach diesen Anweisungen existieren und wie viele Plätze der Reihung becken den Wert null haben.

Ansatz: new Fisch[6] erzeugt nur sechs leere Plätze, noch keinen einzigen Fisch.
Stolperstelle: Zeile 4 enthält kein new. Es wird nur eine Referenz kopiert. Und f steht gar nicht in der Reihung.
Vollständige Lösung

Objekte entstehen nur durch new Fisch(...): Guppy, Neon, Wels → 3 Objekte. In der Reihung sind die Plätze 0, 2 und 5 belegt (0 und 5 verweisen auf denselben Guppy), die Plätze 1, 3 und 4 sind null.

Die Reihung selbst ist zwar auch ein Objekt, aber kein Fisch-Objekt. Ein Zugriff wie becken[1].getArt() würde eine NullPointerException auslösen.

A9
Assoziation lesen: Escape-Room
AFB I

In der Steuerung eines Escape-Rooms kennt jeder Raum sein Schloss.

Raum
  • - name: Zeichenkette
  • - schloss: Schloss
  • c Raum(name: Zeichenkette, schloss: Schloss)
  • + getSchloss(): Schloss
  • + betreten(): Wahrheitswert
hat11
Schloss
  • - code: Zeichenkette
  • - offen: Wahrheitswert
  • c Schloss(code: Zeichenkette)
  • + oeffnen(versuch: Zeichenkette)
  • + istOffen(): Wahrheitswert

Beschreiben Sie, wie die Assoziation „hat“ im Quelltext umgesetzt wird und was beim Aufruf r.getSchloss().oeffnen("4711") geschieht. Gehen Sie auch darauf ein, was passiert, wenn das Attribut schloss den Wert null hat.

Ansatz: Suche in der Karte Raum ein Attribut, dessen Typ eine andere Klasse ist.
Aufrufkette: Lies von links nach rechts: Zuerst liefert r.getSchloss() eine Referenz, dann wird auf diesem Objekt oeffnen aufgerufen.
Vollständige Lösung

Die Assoziation wird durch das Attribut - schloss: Schloss in Raum umgesetzt: Ein Raum speichert eine Referenz auf ein Schloss-Objekt, das ihm im Konstruktor übergeben wird. Das Schloss selbst kennt den Raum nicht.

public class Raum {
    private String name;
    private Schloss schloss;

    public Raum(String name, Schloss schloss) {
        this.name = name;
        this.schloss = schloss;
    }

    public Schloss getSchloss() {
        return schloss;
    }

    public boolean betreten() {
        return schloss.istOffen();
    }
}

Bei r.getSchloss().oeffnen("4711") liefert getSchloss() die Referenz auf das Schloss des Raums r. Auf diesem Objekt wird oeffnen("4711") aufgerufen: Stimmt der Versuch mit code überein (Vergleich mit equals), wird offen auf true gesetzt. Danach liefert r.betreten() den Wert true, weil der Raum die Anfrage an sein Schloss weitergibt (Delegation).

Ist schloss gleich null, liefert getSchloss() null, und der Aufruf von oeffnen auf null führt zur NullPointerException.

A10
== oder equals? Quiz-Auswertung
AFB I

Eine Quiz-App vergleicht die Eingabe mit der hinterlegten Lösung. Die Eingabe entsteht zur Laufzeit aus Teilstücken.

String loesung = "Paris";
String eingabe = "PARIS".substring(0, 1) + "aris";
int punkte = 0;
if (eingabe == loesung) {
    punkte = punkte + 1;
}
if (eingabe.equals(loesung)) {
    punkte = punkte + 2;
}
if (eingabe.toUpperCase().equals("PARIS")) {
    punkte = punkte + 4;
}

Wenden Sie die drei Vergleiche auf die Zeichenketten an und geben Sie den Wert von punkte am Ende an.

Ansatz: Welchen Text enthält eingabe? Setze "P" und "aris" zusammen.
Wichtig: == vergleicht bei Objekten die Referenzen. Eine Zeichenkette, die erst zur Laufzeit zusammengesetzt wird, ist ein neues Objekt — auch wenn der Inhalt gleich ist.
Letzter Vergleich: toUpperCase() liefert eine neue Zeichenkette in Großbuchstaben; equals vergleicht den Inhalt.
Vollständige Lösung

eingabe enthält „Paris“, ist aber ein neues String-Objekt. Deshalb ist eingabe == loesung false (verschiedene Referenzen) → keine Punkte. eingabe.equals(loesung) vergleicht den Inhalt → true, +2. "PARIS".equals("PARIS") → true, +4.

punkte = 0 + 2 + 4 = 6. Wer 7 erhält, hat == fälschlich als Inhaltsvergleich gelesen — genau deshalb vergleicht man Zeichenketten immer mit equals.