Übung
Als Programmierer_innen können wir so gut wie alles modellieren. Wie wäre es einmal mit… einem Hund?
Implementiere die Klasse Hund so wie im Klassendiagramm gezeigt. Wenn der Hund schwerer als 15 kg ist, soll beim Bellen »WUFF WUFF!« ausgegeben werden, ansonsten “Waff waff!".
Lege einen Workspace »Hunde« an und dort eine Datei »Hund.java«. Beginne mit der Klassendefinition:
public class Hund {
}
Kommst du nun alleine weiter?
Dafür kannst du mehr oder weniger die Zeilen aus der Klassenkarte abschreiben, du musst nur das -
durch das Java-Schlüsselwort private
ersetzen:
public class Hund {
private String name;
private int gewicht;
}
Schaffst du es von hieraus alleine weiter?
Das stimmt! Der Konstruktor ist eine besondere Methode. Wir dürfen keinen Rückgabetyp angeben, weil ja ein Objekt der Klasse selbst zurückgegeben wird.
Dieser Konstruktor nimmt den Namen des anzulegenden Hund-Objekts entgegen. (Tipp: Lasse eine Zeile nach den Attributen frei, und schreibe den Konstruktor dort hin! Achte darauf, dass du ihn aber innerhalb der geschweiften Klammern der Klasse aufschreibst!)
// Konstruktor
public Hund(String name) {
}
Kommst Du mit der Zuweisung des Namens zu Instanzvariable name
alleine zurecht?
Ja, das ist richtig! Wir haben ja die Instanzvariable name
weiter oben aufgeschrieben (»deklariert«), und nun weisen wir ihr im Konstruktor einen Wert zu. Dieser Wert kann bei jeder neuen Objekterzeugung verschieden sein – so wie viele Hunde verschiedene Namen haben. Die Variable name
, die dem Konstruktor in den Klammern übergeben wird, ist allerdings nur eine lokale Variable – nachdem der Konstruktor das Objekt erzeugt hat, wird sie wieder entsorgt.
Nun ja, alles endet irgendwann einmal… Übrigens haben sich für diesen Abholdienst die Java-Macher den schönen Namen »Garbage collector« ausgedacht – ganz passend, oder?
Auf jeden Fall »lebt« die übergebene Variable name
nur kurz. Wir wollen den Namen aber vor ihrem Ende in der Instanzvariable abspeichern, und das geht so:
//Konstruktor
public Hund(String name) {
this.name = name;
}
Wie sieht es denn mit der Getter-Methode aus. Brauchst du da Hilfe?
Für den Methodenkopf können wir im Prinzip wieder die Zeile »+String getName()« mehr oder weniger abschreiben (dabei wird das +
zu public
):
(Lasse nach dem Konstruktor eine Zeile frei, und schreibe dort den Methodenkopf hin. Achte wieder darauf, dass du innerhalb der geschweiften Klammern der Klasse bleibst.)
// Getter-Methode für name
public String getName() {
}
Wir geben String
als Rückgabetyp an (der Name ist ja eine Zeichenkette) und lassen den Platz in den Klammern leer, weil wir dem Hund-Objekt hier nichts mitteilen wollen, sondern nur den Namen wissen möchten.
Jetzt fehlt noch eine Zeile – bekommst du die hin?
»Zurückgeben« ist genau das richtige Stichwort! Das Java-Schlüsselwort dafür ist return
, und danach kommt das, was zurückgegeben wird:
// Getter-Methode für name
public String getName() {
return this.name;
}
Wie sieht es mit der Setter-Methode aus?
Hier gibt es keinen Rückgabetyp, deshalb verwenden wir das Schlüsselwort void
für »Leere«. Allerdings nehmen wir das Gewicht des Hundes als ganze Zahl entgegen. Damit sieht unser Methodenkopf so aus:
(Lasse nach der getName
-Methode eine Zeile frei, und schreibe dort den Methodenkopf hin. Achte wieder darauf, dass du innerhalb der geschweiften Klammern der Klasse bleibst.)
// Setter-Methode für das Gewicht
public void setGewicht(int gewicht) {
}
Weißt du, wie du das Gewicht setzen kannst?
Auch hier speichern wir einen Wert, der übergeben wird (int gewicht
) in einer Instanzvariable ab. Das geht in einer Zeile:
// Setter-Methode für das Gewicht
public void setGewicht(int gewicht) {
this.gewicht = gewicht;
}
Kannst du den Hund denn bellen lassen?
Dazu brauchen wir eine Fallunterscheidung mit if
und else
. Schreibe zunächst den Methodenkopf:
public void belle() {
}
Wenn das Gewicht des Hundes schwerer als 15 kg ist, soll er laut bellen (»WUFF WUFF«), also überprüfen wir, ob das der Fall ist:
public void belle() {
if (this.gewicht > 15) {
}
}
Genau! Im darauffolgenden Block geben wir nun an, was dann passieren soll. (Der Hund soll »WUFF WUFF« ausgeben.)
public void belle() {
if (this.gewicht > 15) {
println("WUFF WUFF");
}
}
Wenn der Hund nicht schwerer als 15 kg ist, dann ist er leichter (oder gleichschwer). Wir müssen also keine weiteren Vergleiche anstellen, sonder können einfach else
(»andernfalls«) schreiben:
public void belle() {
if (this.gewicht > 15) {
println("WUFF WUFF");
} else {
println("Waff waff");
}
}
Jetzt ist die Klasse fertig!
Sehr schön! Nun hast du also eine Klasse Hund
geschrieben.. Bist du bereit, die Hunde bellen zu lassen?
Zuvor zwei kleine Quizfragen: mit welcher Zeile lässt sich ein neues Hund
-Objekt erzeugen und in der neu angelegten Variable hundi
abspeichern?
Das ist leider falsch… Hund hundi
deklariert ganz richtig die Variable, doch ein neues Objekt bekommen wir nur mit dem Schlüsselwort new
. Richtig ist also:
Hund hundi = new Hund("Bello");
Das ist leider falsch… ein neues Objekt bekommen wir nur mit dem Schlüsselwort new
. Richtig ist also:
Hund hundi = new Hund("Bello");
Sehr schön! Gut gemerkt!
Das ist leider falsch… Hund hundi
deklariert ganz richtig die Variable, doch ein neues Objekt bekommen wir nur mit dem Schlüsselwort new
. Richtig ist also:
Hund hundi = new Hund("Bello");
Und noch: wie lässt sich das Gewicht des Hundes setzen?
Das würde funktionieren, wenn das Attribut gewicht
öffentlich (also public
) wäre. Das ist aber nicht der Fall. (Dies ist üblich, um Manipulationen zu vermeiden. Obwohl wir das nicht implementiert haben, könnten wir in unserer Methode setGewicht
kontrollieren, dass beispielsweise kein negatives oder kein übermäßig schweres Gewicht gesetzt wird.) Richtig ist
hundi.setGewicht(21);
Gut gemacht!
Bis auf das =
sind die richtigen Zeichen alle da, leider stimmt die Reihenfolge nicht. Richtig ist:
hundi.setGewicht(21);
Erzeuge nun (in einer Datei »START«) einige Hund
-Objekte, setze verschiedene Gewichte, und lasse sie bellen!
Sehr schön!
Wenn du dir etwas Zeit dafür nehmen möchtest, überlege und implementiere weitere sinnvolle Attribute und Methoden für die Klasse Hund.
Sehr schön!
In Ordnung.
Jetzt wirst du lernen, was
Referenzen
sind. Lege im gleichen Workspace, in dem du die Klasse Hund
programmiert hast, eine Datei HalterIn.java an, und übernimm darin diesen Code:
public class HalterIn {
private Hund hund;
public void setHund(Hund hund) {
this.hund = hund;
}
public void gassiGehen() {
println("Ich gehe Gassi mit " + this.hund.getName());
this.hund.belle();
}
}
Sehr schön! Erzeuge nun ein Objekt dieser Klasse, und versuche, die Methode gassiGehen
aufzurufen. Der Code in deiner Start-Datei sollte nun in etwa so aussehen:
Hund h = new Hund("Bello");
h.setGewicht(20);
HalterIn tonyHoare = new HalterIn();
tonyHoare.gassiGehen();
Was passiert, wenn du das Programm so laufen lässt?
Überrascht?
Dazu kommen wir gleich. Führe die Datei erst einmal Schritt für Schritt aus (Taste F6
), und untersuche die Attribute der erzeugten Objekte. Versuche zu verstehen, wo das Problem liegen könnte.
So ist es! Bevor wir das Problem lösen, übernimm bitte diesen Eintrag in dein Heft:
Referenzen
Variablen können primitive Datentypen enthalten. Das sind z.B. die Typen int
, double
oder boolean
, die entsprechenden Werte werden direkt in der Variable gespeichert. Bei der Deklaration als Instanzvariable werden solche Variablen in Java mit Default-Werten (0
, 0.0
, false
) belegt.
Daneben können Variablen aber auch Zeiger (auch Pointer, oder Referenzen) enthalten. In diesem Fall zeigen die Variablen auf Objekte, die dadurch ansprechbar werden. Bei der Deklaration solcher Variablen werden diese zunächst mit dem Wert null
belegt.
Gut! Damit unser Hund
-Objekt Gassi gehen kann, muss das HalterIn
-Objekt das Hund
-Objekt kennen. (Wir legen den Hund sozusagen an die Leine.) Füge dazu die Zeile tonyHoare.setHund(h)
wie unten gezeigt in deine Start-Datei ein.
Hund h = new Hund("Bello");
h.setGewicht(20);
HalterIn tonyHoare = new HalterIn();
tonyHoare.setHund(h);
tonyHoare.gassiGehen();
Sehr schön! Wenn du noch Zeit hast, widme dich doch diesen…
…Aufgaben
Mit this.hund == null
kannst Du überprüfen, ob das Referenzattribut hund
schon auf ein Hund
-Objekt verweist. (Mit this.hund != null
lässt sich sicherstellen, dass schon ein Hund
-Objekt referenziert wird.)
Verbessere die Klasse HalterIn
. Beziehe folgende Fragen in Deine Verbesserung mit ein:
Was soll passieren, wenn jemand ohne Hund Gassi geht?
Was soll passieren, wenn ein neuer Hund zugewiesen werden soll, aber bereits ein anderer Hund zugeordnet ist?
Soll ein Hund auch wieder zurückgegeben werden können?
Könnte jemand auch mit mehr als einem Hund Gassi gehen?
Optionale Aufgabe
Lies dir den Artikel über Tony Hoare in der englischen Wikipedia durch, und finde heraus, was sein »billion dollar mistake« war.