04.03.04 Umgang mit mehreren Klassen

Die Theorie zu Attributen und Methoden haben Sie – erst einmal – überstanden. Wir wollen uns das Gelernte an einem etwas größeren Beispiel anschauen. Wir werden Schritt für Schritt die benötigten Klassen erstellen, die Verwendung der Konsole zum kompilieren und ausführen nutzen und uns mit den Techniken im Hintergrund beschäftigen.

Durst im Sommer

Der Rahmen unseres Beispieles ist ein gemütlicher Nachmittag im Englischen Garten in München. Man liegt auf der Wiese und hat sich einen zuvor gefüllten Bierkasten mitgebracht um der Sommerhitze Paroli bieten zu können. Der Kasten selber enthält einzelne Bierflaschen verschiedenster Art, welche wir im Laufe des Nachmittags zur Abkühlung konsumieren wollen.
Aus dieser kurzen Beschreibung wollen wir uns nun ein Programm bauen, welches einen Bierkasten mit Flaschen befüllen kann und mit dem man auch einzelne Flaschen herausnehmen kann, um sie zu trinken. Aus der Beschreibung geht hervor, dass wir wahrscheinlich zwei Klassen benötigen werden – eine Klasse Bierkasten und eine Klasse Bierflasche. Zusätzlich testen wir dies dann alles in der Klasse EnglischerGarten. Alle diese Klassen speichern wir in einem Verzeichnis ab.

Bierflasche

Fangen wir zunächst mit der Bierflasche an. Welche Eigenschaften hat so eine Bierflasche?
In unserem Fall reicht es, wenn die Flasche einen Namen hat und wir den aktuellen Zustand (voll oder leer) wissen. Somit können wir den Zustand unserer Bierflasche über die Attribute name und istLeer beschreiben. Weiterhin soll es möglich sein einer Flasche einen Namen zu geben und auch diese Flasche zu trinken. Zum Abschluss soll uns eine weitere Methode über den Zustand in Textform informieren.

Wir haben also für unsere Klasse Bierflasche folgende Elemente.

Attribute

  • name
  • istLeer

Methoden

  • setzeName
  • trinkeFlasche
  • zustand

Der name soll aus Buchstaben bestehen, weswegen wir hierfür den Typ String verwenden. Für das Attribut istLeer reicht die Verwendung des Typs boolean, welches angibt, ob die Flasche leer ist oder nicht. Die Methode setzeName soll den Namen der Flache setzen. Also benötigen wir einen Parameter vom Typ String mit dem Namen. Mit trinkeFlasche wird der Zustand der Flasche auf „leer getrunken“ gesetzt. Sie benötigt also keinen Übergabeparameter. Die Methode zustand liefert uns eine Textbeschreibung und muss demzufolge einen Rückgabewert vom Typ String besitzen. Nach diesen Vorbetrachtungen können wir nun die Klasse Bierflasche schreiben.

public class Bierflasche {

  private String name;
  private boolean istLeer = false;

  public void setzeName(String aName) {
    this.name = aName;
  }

  public void trinkeFlasche() {
    this.istLeer = true;
  }

  public String zustand() {

    String fuellstand = "";

    if (this.istLeer) {
      fuellstand = "leere";
    }
    else {
      fuellstand = "volle";
    }

    return "eine " + fuellstand + " Flasche " + this.name;
  }
}

Die beiden Methoden setzeName und trinkeFlasche dürften selbst erklärend sein, da sie lediglich die Werte der Attribute name und istLeer verändern. Die Methode zustand überprüft, ob die Flasche leer ist oder nicht und gibt uns dementsprechend einen String zurück.

Bierkasten

Analog zu den Vorüberlegungen zu der Bierflasche wollen wir dies nun auch für den Bierkasten tun. Ein Bierkasten besteht aus einer bestimmten Anzahl von Fächern, welche Flaschen beinhalten können. Weiterhin soll es möglich sein, dass wir den Bierkasten befüllen und einzelne Flaschen entnehmen können. Damit wir wissen wo noch Platz für weitere Flaschen im Kasten ist, brauchen wir ein Attribut, welches sich die aktuelle Position merkt. Der Bierkasten selbst soll uns per Textausgabe über seinen aktuellen Zustand informieren. Daraus ergeben sich folgende Elemente für diese Klasse.

Attribute

  • inhalt
  • position

Methoden

  • setzeInhalt
  • neueFlasche
  • gibFlasche
  • ausgabe

Der inhalt besteht aus einem Feld von Elementen des Typs Bierflasche und wird somit als Array deklariert. Das Attribut position zeigt immer auf das nächste leere Fach innerhalb unseres Bierkastens. Da es sich dabei um ganze Zahlen handelt, ist dieses Attribut vom Typ int. Die Methode setzeInhalt soll es ermöglichen die maximal mögliche Anzahl an Fächern zu setzen. Dies erfolgt per Übergabe eines Parameters vom Typ int. Mit neueFlasche wird eine neue Flasche in den Kasten gestellt, dazu braucht diese Methode einen Parameter vom Typ Bierflasche. Um eine Flasche aus dem Kasten zu nehmen, benötigen wir die Angabe der Position aus welchem Fach die Flasche entnommen werden soll. Zusätzlich wird diese Flasche noch zurückgegeben. Unsere Methode gibtFlasche enthält also einen Parameter vom Typ int für die Position und als Rückgabetyp Bierflasche. Mit ausgabe soll dann der Inhalt unseres Bierkastens auf der Konsole ausgegeben werden. Sie benötigt dafür keine Parameter. Dies setzen wir nun alles in einer Klasse namens Bierkasten um.

public class Bierkasten {

  private Bierflasche[] inhalt;
  private int position = 0;

  public void setzeInhalt(int aInhalt) {
    this.inhalt = new Bierflasche[aInhalt];
  }

  public void neueFlasche(Bierflasche aFlasche) {

    this.inhalt[this.position] = aFlasche;
    this.position++;
  }

  public Bierflasche gibFlasche(int aPos) {
    return this.inhalt[aPos];
  }

  public void ausgabe() {

    System.out.println("Dieser Bierkasten enthaelt:\n");

    for (int i = 0; i < this.inhalt.length; i++) {

      System.out.println(this.inhalt[i].zustand());
    }

    System.out.println();
  }
}

Das Attribut inhalt ist ein Array mit dem Typ Bierflasche, dies ist somit unser Container für die einzelnen Flaschen. Mit position soll immer auf das nächste freie Fach im Kasten gezeigt werden. Dies ist zu Beginn stets das Fach 0. Mit setzeInhalt wird die maximale Anzahl an Fächern gesetzt. Das heißt, unser Bierkasten kann maximal aInhalt viele Bierflaschen enthalten. Die Methode neueFlasche soll die als Parameter übergebene Bierflasche in das nächste freie Fach abstellen. Dazu wird die nächste freie Stelle des Arrays inhalt bestimmt und die übergebene Flasche dort untergebracht. Danach wird die Position des nächsten freien Faches um eins erhöht. Sollte Ihnen bei der Arbeit mit Arrays noch etwas unklar sein, lesen Sie bitte noch einmal Kapitel 02.09 Arrays. Mit gibFlasche wird uns die Flasche an der übergebenen Position aPos des Arrays inhalt zurück gegeben. Die ausgabe gibt den kompletten Inhalt des Bierkastens aus, indem wir mit einer for-Schleife über das Attribut inhalt iterieren. Innerhalb dieser Schleife holen wir uns die einzelnen Elemente vom Typ Bierflasche und verwenden deren Methode zustand für die Ausgabe.

Englischer Garten

Wir wollen nun, nach Fertigstellung der Klassen Bierflasche und Bierkasten, diese auch testen. Dazu schreiben wir eine dritte Klasse mit dem Namen EnglischerGarten.

public class EnglischerGarten {

  public static void main(String[] args) {

    // Kasten aus dem Keller holen
    Bierkasten kasten = new Bierkasten();
    kasten.setzeInhalt(3);

    // Im Kühlschrank schauen, was noch an Bier da ist
    Bierflasche hacker = new Bierflasche();
    Bierflasche augustiner = new Bierflasche();
    Bierflasche dachs = new Bierflasche();

    hacker.setzeName("Hacker-Pschorr - Hubertus Bock");
    augustiner.setzeName("Augustiner Edelstoff");
    dachs.setzeName("Dachsbraeu - Ulimator");

    // gefundenes Bier in den Kasten stellen
    kasten.neueFlasche(hacker);
    kasten.neueFlasche(augustiner);
    kasten.neueFlasche(dachs);

    // in den Kasten schauen
    kasten.ausgabe();

    // ein Bier trinken
    Bierflasche bier = kasten.gibFlasche(1);
    bier.trinkeFlasche();

    // wieder in den Kasten schauen
    kasten.ausgabe();
  }
}

Begonnen wird mit dem Erzeugen eines Bierkastens namens kasten. Danach setzen wir den Inhalt auf maximal drei Flaschen. Zum einen soll der Tag im Englischen Garten uns in guter Erinnerung bleiben und zum anderen soll dieses Beispiel kompakt und übersichtlich bleiben.
Wir schauen nun nach, was wir noch an Bier da haben, um es in den Kasten zu stellen. Dies sind hacker, augustiner und dachs. Alle Bierflaschen bekommen einen Namen und werden in den Kasten gestellt. Danach lassen wir uns den Inhalt des Kastens anzeigen. Um unseren Durst zu stillen, holen wir uns aus dem Kasten mit der Methode gibFlasche die Flasche an der Fachposition 1. Dieses Bier trinken wir. Mit der erneuten Ausgabe des Kasteninhaltes sehen wir die Veränderung.

Kompilierung und Ausführung

Wie zu Beginn erwähnt, sollten alle Klasse in einem gemeinsamen Verzeichnis gespeichert werden. Dieses sollte nach Fertigstellung unserer drei Klassen folgenden Inhalt haben.

  • Bierflasche.java
  • Bierkasten.java
  • EnglischerGarten.java

Wir möchten nun diese Quellcode Dateien zu Java Bytecode kompilieren.
Dazu öffnen Sie bitte ihr Terminal bzw. ihre Konsole und wechseln in das entsprechende Verzeichnis.

Sind wir im richtigen Verzeichnis angelangt, benutzen wir den Java-Compiler javac.

javac *.java

Wir lassen uns mit javac *.java gleich alle Dateien kompilieren, welche die Endung „java“ besitzen. Unser Verzeichnis bekommt daraufhin drei weitere Dateien.

  • Bierflasche.class
  • Bierkasten.class
  • EnglischerGarten.class

Um unser Programm nun zu starten, müssen wir dem Java Interpreter java die Klasse als Argument überreichen, welche die main Methode enthält.

java EnglischerGarten

Beachten Sie bitte, dass Sie bei der Angabe der Startklasse keine Endung mit angeben.
Dies würde einen Fehler erzeugen. Hiermit versucht der Interpreter die Klasse class in einem Unterverzeichnis „EnglischerGarten“ zu finden, welche ja nicht existiert. Mehr dazu finden Sie in Kapitel 04.03.10 Pakete.

java EnglischerGarten.class
Exception in thread "main" java.lang.NoClassDefFoundError: EnglischerGarten/class

Haben wir alles richtig gemacht, erscheint zweimal die Ausgabe unseres Bierkasteninhaltes.

java EnglischerGarten
Dieser Bierkasten enthaelt:

eine volle Flasche Hacker-Pschorr - Hubertus Bock
eine volle Flasche Augustiner Edelstoff
eine volle Flasche Dachsbraeu - Ulimator

Dieser Bierkasten enthaelt:

eine volle Flasche Hacker-Pschorr - Hubertus Bock
eine leere Flasche Augustiner Edelstoff
eine volle Flasche Dachsbraeu - Ulimator

Sie sehen an der zweiten Ausgabe, dass wir die zweite Flasche im Kasten leeren konnten.

Fazit

Dies war einmal ein etwas ausführlicheres Beispiel zu den Klassen. Ziel war es Ihnen zu zeigen wie Sie mit mehreren Klassen gleichzeitig arbeiten können. Die Verwendung unterschiedlicher Objektmethoden mit und ohne Parameter bzw. Rückgabetyp wurde ebenfalls demonstriert. Weiterhin haben wir unsere Attribute in den Klassen mit dem Schlüsselwort private gekapselt, um zu verhindern, dass man von außerhalb darauf zugreifen kann, um diese etwa zu ändern. Wir verwenden in diesem Beispiel stets Methoden die diesen Zugriff übernehmen (siehe Kapitel 03.04.06 Sichtbarkeitsmodifizierer). Zusätzlich haben wir gesehen, wie eine selbst geschriebene Klasse eine andere selbst geschriebene benutzen kann. Dies war der Fall, als wir den Inhalt der Klasse Bierkasten als Array des Typs Bierflasche deklariert haben.
Ebenso sollte der Verlauf der Quellcode Erstellung, dem Kompilieren und der Ausführung von Programmen auf der Konsole noch einmal vor Augen geführt werden.

Um dieses Beispiel kompakt und übersichtlich zu halten, wurde auf notwendige Sicherheitsüberprüfungen verzichtet. Zum Beispiel müssten alle Zugriffe auf ein Array dahin gehend überprüft werden, ob diese nicht über die Arraygrenzen hinaus darauf zugreifen.

2 Replies to “04.03.04 Umgang mit mehreren Klassen”

  1. RolandF

    Hi Stefan
    Beim Bierkasten
    22. System.out.println(„Dieser Bierkasten enthaelt: n“);

    das ’n‘ am Schluß soll sicher ein ‚ \n‘ sein oder ?

  2. Stefan Kiesel

    Hi Roland,

    da haben wir aber einen aufmerksamen Leser gewonnen. Danke für den Hinweis, das ist natürlich vollkommen korrekt! Ich habe das Kapitel ausgebessert.

    Gruß
    Stefan

Schreibe einen Kommentar

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.