MINT lernen

Übungen: Primitive Typen und Referenzen

Zwei Variablen, ein Objekt: Wer den Unterschied zwischen Wert und Referenz kennt, findet Fehler, die der Compiler nie meldet.

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
Wert oder Referenz? Der Fitnesstracker
AFB I

Die App eines Fitnesstrackers enthält diese Deklarationen. Was steht jeweils in der Variablen selbst?

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).
1speichert direkt einen Wert
2speichert eine Referenz
Nur die acht primitiven Typen wie int, double, boolean und char speichern den Wert direkt. Alles andere ist ein Objekttyp — die Variable enthält nur einen Verweis auf das Objekt. Typische Fehler: String zu den Werten legen, weil man Zeichenketten so bequem wie Zahlen schreibt, und int[] übersehen: Eine Reihung ist auch ein Objekt, selbst wenn sie Ganzzahlen enthält (Kapitel 2). Und reserve ist eine Referenzvariable, die gerade auf nichts zeigt.
Ansatz: Frage dich bei jeder Zeile: Ist der Typ einer der primitiven Typen int, double, boolean oder char?
Weiter: String beginnt mit einem Großbuchstaben — es ist eine Klasse. Und eine Reihung wird mit new erzeugt, ist also ein Objekt.
A2
Zwei Kontakte, drei Variablen
AFB I

Ein digitales Adressbuch speichert Kontakte. Welche Aussagen über diese drei Zeilen stimmen?

Kontakt k1 = new Kontakt("Mila", "0151 223344");
Kontakt k2 = k1;
Kontakt k3 = new Kontakt("Mila", "0151 223344");
Mehrere Antworten sind richtig. Markiere alle zutreffenden und klicke dann auf „Prüfen“.
Nur new erzeugt Objekte — zwei new, zwei Objekte. k2 = k1 kopiert die Referenz, nicht das Objekt: k2 ist ein zweiter Name (Alias) für dasselbe Objekt. == vergleicht bei Objekten, ob zwei Referenzen auf dasselbe Objekt zeigen — gleiche Werte reichen nicht. Und k1 = null; ändert nur die Variable k1; k2 verweist weiter auf das Objekt.
Ansatz: Zähle zuerst die new. Überlege dann, was bei k2 = k1 wirklich kopiert wird.
Weiter: Stell dir jede Variable als Zettel mit einer Adresse vor. == vergleicht die Adressen, nicht den Inhalt der Objekte.
A3
Fachbegriffe am Wecker
AFB I

Ein Smartphone-Wecker speichert die Schlummerzeit als Zahl und den Wecker selbst als Objekt. Setze die Fachbegriffe ein — einer bleibt übrig.

int schlummer = 9;
Wecker w = new Wecker("06:45");
Wecker morgen = w;
Wecker urlaub = null;
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.

Die Variable schlummer speichert direkt den 9. Die Variable w speichert dagegen eine auf ein Wecker-Objekt. Das Objekt selbst entsteht erst durch . Die Variable urlaub verweist auf kein Objekt, sie hat den Wert . Nach Wecker morgen = w; ist morgen nur ein zweiter Name für dasselbe Objekt — ein .

equals bleibt übrig: Es vergleicht den Inhalt zweier Objekte (etwa zweier Zeichenketten) und kommt in diesen vier Zeilen gar nicht vor. Häufiges Missverständnis: morgen sei eine Kopie des Weckers. Stellt man über morgen die Weckzeit um, klingelt aber auch w zur neuen Zeit — es ist ja dasselbe Objekt.
Ansatz: Primitive Typen speichern Werte, Objekttypen speichern Verweise.
Weiter: Ein zweiter Name für dasselbe Objekt heißt Alias. Eine Variable, die auf nichts zeigt, hat den Wert null.
A4
Stimmt's? — Das WLAN-Passwort
AFB II

Ein Router prüft das eingegebene WLAN-Passwort. Zum Test werden diese Zeichenketten angelegt (new String(...) erzeugt ausdrücklich ein neues String-Objekt, so wie es auch beim Einlesen einer Tastatureingabe geschieht):

String passwort = "LUX7";
String eingabe = new String("LUX7");
String kopie = passwort;
Fünf Aussagen nacheinander. Eine falsche Einschätzung reicht — dann startest du die Serie mit „Neue Runde“ neu.
Aussage 1 von 5

Merksatz: Zeichenketten immer mit equals vergleichen. == prüft bei Objekten nur, ob beide Variablen auf dasselbe Objekt zeigen. Das kann bei zwei gleichen String-Literalen zufällig true liefern und verleitet so zu einem Fehler, der erst bei echten Eingaben auffällt.
Ansatz: Überlege bei jedem Vergleich: Werden Werte, Referenzen oder Inhalte verglichen?
Weiter: new String(...) erzeugt immer ein eigenes Objekt. Und an einer null-Referenz kann keine Methode aufgerufen werden.
A5
Was passiert im Speicher? — Wagen eines Zuges
AFB II

Die Klasse Wagen eines Zuges hat den Konstruktor Wagen(plaetze: Ganzzahl) sowie getPlaetze() und setPlaetze(p). Die Zeilen links werden nacheinander ausgeführt. Verbinde jede Zeile mit ihrer Wirkung.

Ansatz: Achte darauf, ob in der Zeile new vorkommt — nur dann entsteht ein Objekt.
Weiter: Eine Zuweisung an eine Variable (w = new Wagen(80)) ändert kein Objekt, sondern nur, wohin die Variable zeigt. Ein Methodenaufruf (v.setPlaetze) ändert das Objekt.
A6
Preiserhöhung beim Kanuverleih
AFB IIMix

Ein Kanuverleih erhöht seine Preise. Die Hilfsklasse bekommt einmal eine einzelne Zahl und einmal eine Reihung aus Kapitel 2 übergeben:

public class Tarifrechner {
    public void erhoehen(int preis) {
        preis = preis + 5;
    }

    public void erhoehenAlle(int[] preise) {
        for (int i = 0; i < preise.length; i++) {
            preise[i] = preise[i] + 5;
        }
    }
}
Tarifrechner v = new Tarifrechner();
int grundpreis = 12;
int[] tarife = {12, 18, 25};
v.erhoehen(grundpreis);
v.erhoehenAlle(tarife);
v.erhoehen(tarife[0]);

Welchen Wert hat grundpreis + tarife[0] + tarife[2] am Ende?

Rechne selbst und trage das Ergebnis ein — Enter prüft direkt.
Beim Aufruf wird immer der Inhalt der Variablen übergeben. Bei int ist das der Wert 12 — erhoehen ändert nur seine lokale Kopie preis, grundpreis bleibt 12. Bei int[] ist der Inhalt eine Referenz: erhoehenAlle arbeitet auf derselben Reihung, tarife wird zu {17, 23, 30}. Der dritte Aufruf übergibt wieder nur den Wert tarife[0] = 17 und ändert nichts. Also 12 + 17 + 30 = 59. Häufige Fehler: 69 (jede Erhöhung wirkt) oder 49 (auch die Reihung bleibt unverändert).
Ansatz: Unterscheide die beiden Methoden: Was wird bei int übergeben, was bei int[]?
Weiter: grundpreis bleibt 12. Die Reihung wird tatsächlich verändert. Und v.erhoehen(tarife[0]) bekommt nur eine Zahl, keine Referenz auf die Reihung.
A7
Figuren auf dem Schachbrett
AFB II

Eine Schach-Lern-App speichert zu jeder Figur die Spalte (1 bis 8). ziehe(felder) addiert die Felder zur Spalte, getSpalte() liefert sie.

Figur a = new Figur("Turm", 1);
Figur b = new Figur("Springer", 2);
Figur c = new Figur("Laeufer", 3);
Figur e = new Figur("Koenig", 6);
Figur d = a;
d.ziehe(3);
d = b;
d.ziehe(3);
b = c;
b.ziehe(-1);
c = new Figur("Dame", 8);
c.ziehe(-1);

Sortiere die Aufrufe nach ihrem Rückgabewert am Ende — der kleinste zuerst.

Ziehe die Karten in die richtige Reihenfolge — mit der Tastatur: ↑/↓ verschiebt, Shift+↑/↓ wechselt nur den Fokus.
1b.getSpalte()
2a.getSpalte()
3d.getSpalte()
4e.getSpalte()
5c.getSpalte()
Verfolge Objekte, nicht Variablennamen: Turm 1 → 4 (über d bewegt), Springer 2 → 5 (über d bewegt), Läufer 3 → 2 (über b bewegt), Dame 8 → 7, König bleibt 6. Am Ende zeigt a auf den Turm (4), b auf den Läufer (2), c auf die Dame (7), d auf den Springer (5), e auf den König (6). Typischer Fehler: a für unverändert halten (1), weil nie a.ziehe aufgerufen wurde — d war aber ein Alias für a.
Ansatz: Zeichne vier Kästchen für die Objekte und setze für jede Variable einen Pfeil. Ändere bei jeder Zeile entweder einen Pfeil oder eine Zahl im Kästchen.
Weiter: d zeigt zuerst auf den Turm, später auf den Springer. b zeigt ab b = c auf den Läufer. ziehe wirkt immer auf das Objekt, auf das die Variable gerade zeigt.
A8
Leihräder und ihre Kilometer
AFB III

Ein Fahrradverleih zählt die gefahrenen Kilometer jedes Rades.

public class Leihrad {
    private String nummer;
    private int km;

    public Leihrad(String nummer) {
        this.nummer = nummer;
        km = 0;
    }

    public void fahren(int strecke) {
        km = km + strecke;
    }

    public int getKm() {
        return km;
    }
}
Leihrad r1 = new Leihrad("R-07");
Leihrad r2 = new Leihrad("R-12");
Leihrad r3 = r1;
r3.fahren(12);
r1.fahren(5);
r2 = r3;
r2.fahren(8);
r3 = new Leihrad("R-20");
r3.fahren(4);
Rechne die Kette Schritt für Schritt: Erst wenn ein Schritt stimmt, wird der nächste freigeschaltet. Enter prüft.
  1. Rückgabewert von r1.getKm() direkt nach der Zeile r1.fahren(5); km
  2. Rückgabewert von r2.getKm() am Ende km
  3. Wie viele Leihrad-Objekte wurden insgesamt erzeugt? Objekte
  4. Wie viele der drei Variablen verweisen am Ende auf das Rad „R-07“? Variablen
Verlauf: r3 wird Alias von r1 (Rad R-07), also landen 12 und 5 km auf demselben Rad: 17. Nach r2 = r3; zeigt auch r2 auf R-07 — die 8 km kommen dazu: 25. Das Rad R-12 hat keine Variable mehr, die darauf zeigt, existiert aber als drittes Objekt (neben R-07 und R-20). Am Ende zeigen r1 und r2 auf R-07, r3 auf R-20. Häufiger Fehler: r2.getKm() = 8, weil man meint, r2 sei noch das Rad R-12.
Ansatz: Lege eine Tabelle an: Spalten R-07, R-12, R-20 für die Kilometer und daneben, auf welches Rad r1, r2, r3 gerade zeigen.
Weiter: Nach Zeile 3 zeigen r1 und r3 auf dasselbe Rad. Nach r2 = r3 zeigen alle drei darauf — bis r3 ein neues Rad bekommt.
A9
Fehlersuche: Der Vokabeltrainer
AFB III

Ein Vokabeltrainer soll drei unabhängige Vokabeln anlegen: Hund/dog, Katze/cat und Maus/mouse. Danach bekommt der Lernende einen Punkt, wenn seine Eingabe antwort (zur Laufzeit eingetippt) zur ersten Vokabel passt. Die Klasse Vokabel hat den Konstruktor Vokabel(deutsch, englisch), Getter und Setter. Drei Zeilen sind falsch — eine davon lässt sich gar nicht übersetzen.

In diesem Text stecken Fehler. Klicke genau die falschen Zeilen an — die richtigen musst du stehen lassen.
Tückisch ist Zeile 2: Das Programm läuft, aber am Ende gibt es nur eine Vokabel Katze/cat, auf die v1 und v2 zeigen — Hund/dog ist verloren. Eine unabhängige Kopie entsteht nur mit new. Zeile 6 kompiliert ebenfalls, liefert bei echter Eingabe aber fast immer false.
Ansatz: Suche nach Stellen, an denen ein Objekt entstehen soll, und nach Vergleichen von Zeichenketten.
Weiter: Zählt, wie viele new im Programm stehen — es sollen drei Objekte werden. Und prüfe, womit die Zeichenketten verglichen werden.
A10
Trickaufgabe: Omas Sparschwein
AFB IIITrick

Oma hilft beim Sparen. Jede Zeile unten startet neu mit Sparschwein p = new Sparschwein(); int bar = 1; Oma o = new Oma(); Welchen Wert liefert der letzte Ausdruck der Zeile? Achte genau darauf, was in den Methoden mit dem Parameter passiert.

public class Sparschwein {
    private int betrag;

    public Sparschwein() {
        betrag = 0;
    }

    public void einwerfen(int euro) {
        betrag = betrag + euro;
    }

    public int getBetrag() {
        return betrag;
    }
}
public class Oma {
    public void schenken(Sparschwein s) {
        s.einwerfen(1);
    }

    public void neuesSchwein(Sparschwein s) {
        s = new Sparschwein();
        s.einwerfen(3);
    }

    public void erhoehen(int zahl) {
        zahl = zahl + 2;
    }
}
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
o.schenken(p); p.getBetrag()
o.neuesSchwein(p); p.getBetrag()
o.erhoehen(bar); bar
Sparschwein q = p; o.schenken(q); o.schenken(p); p.getBetrag()
p.einwerfen(4); o.neuesSchwein(p); p.getBetrag()
Der Parameter s bekommt eine Kopie der Referenz. Über diese Kopie kann schenken das gemeinsame Objekt verändern. neuesSchwein biegt dagegen nur seine lokale Kopie auf ein neues Schwein um — p merkt davon nichts, die 3 Euro landen in einem Objekt, das nach der Methode niemand mehr kennt. Genauso ändert erhoehen nur seine Kopie von bar. Wer 3, 3 und 7 erwartet hat, hat „Parameter ändern“ mit „Objekt ändern“ verwechselt.
Ansatz: Frage bei jeder Methode: Wird das übergebene Objekt verändert (Methodenaufruf an s) oder nur der Parameter neu belegt (s = new Sparschwein())?
Weiter: Eine Zuweisung an einen Parameter wirkt nie nach außen. Ein Methodenaufruf über den Parameter wirkt auf das Objekt, das auch der Aufrufer kennt.