Home > Informatik > Stufe Q1 > Folge 11

11.1 Vokabelliste - Zielsetzung und erster Entwurf

Teil 1 - Teil 2 - Teil 3

Zielsetzung und erste Schritte

Herzlich Willkommen zum ersten größeren Projekt in der Jahrgangsstufe Q1. In der Stufe EF haben Sie hoffentlich schon viel über Java-Programmierung gelernt:

Grundsätzlicher Aufbau eines Java-Programms bzw. einer Java-Anwendung
  • Konstruktor(en)
  • Instanzvariablen, Lokale Variablen
  • Setter- und Getter-Methoden
  • Ausgabemethoden
Fallunterscheidungen und Schleifen
  • if-else-Anweisungen
  • switch-case-Anweisungen
  • while- und for-Anweisungen,
  • eventuell auch do-while-Anweisungen
Arrays
Vererbung und Polymorphie
  • Klassen, Oberklassen, Unterklassen
  • Konzept der Generalisierung und Spezialisierung
  • Polymorphie
Sortieralgorithmen
  • Bubblesort,
  • Selectionsort
  • Insertionsort
Zweidimensionale Arrays

Die Stufe EF haben wir nun hinter uns gelassen und wir können uns jetzt etwas anspruchsvolleren Dingen zuwenden. Als Erstes wollen wir eine kleine Vokabelliste programmieren, in der englische und deutsche Wortpaare verwaltet werden. Zum Speichern der Wortpaare könnte man jetzt natürlich ein Objektarray benutzen, aber das wäre für die Stufe Q1 doch etwas zu einfach. Stattdessen führen wir ein neues Konzept der Java-Programmierung ein, nämlich die sogenannte Arrayliste.

Bevor wir aber mit dem Thema ArrayList beginnen, wollen wir uns eine einfache Klasse Vokabel erstellen, in der deutsche und engliche Begriffspaare gespeichert werden können.

Schritt 1 - Die Klasse Vokabel

Wir beginnen mit einer einfachen Klasse Vokabel. Jedes Objekt dieser Klasse soll ein deutsch-englisches Wortpaar verwalten, beispielsweise Hund / dog.

Attribute der Klasse:

Ein Vokabel-Objekt besitzt zwei Instanzvariablen vom Typ String. Die erste Instanzvariable deutsch speichert das deutsche Wort, die zweite Instanzvariable englisch die englische Übersetzung.

Methoden der Klasse:

Für unsere einfache Vokabel-Klasse benötigen wir zunächst nur wenige Methoden:

  • Einen Konstruktor, der beim Erzeugen eines neuen Vokabel-Objekts die beiden Instanzvariablen mit den übergebenen Wörtern initialisiert.
  • Zwei Getter-Methoden, die das deutsche bzw. das englische Wort als String zurückliefern.
  • Eine toString()-Methode, die das gesamte Wortpaar als String zurückliefert, beispielsweise "Hund / dog".

Die Ausgabe eines Wortpaares

Wie sollen die gespeicherten Wortpaare ausgegeben werden? Sollen sie in der Konsole erscheinen oder später in einem grafischen Element einer Java-Anwendung?

Wir wollen diese Entscheidung nicht der Klasse Vokabel überlassen. Ihre Aufgabe besteht lediglich darin, die beiden Wörter zu speichern und bei Bedarf zur Verfügung zu stellen.

Hier kommt die Methode toString() ins Spiel. Sie gibt das Wortpaar nicht selbst auf dem Bildschirm aus, sondern liefert lediglich einen String zurück. Die aufrufende Anwendung kann dann selbst entscheiden, was mit diesem String geschehen soll.

Eine Besonderheit von toString() ist, dass diese Methode bereits in der Java-Oberklasse Object definiert ist. Wir überschreiben diese geerbte Methode, damit sie das Wortpaar in der gewünschten Form zurückliefert.

Beispielsweise könnte man ein Vokabel-Objekt folgendermaßen auf der Konsole ausgeben:

Vokabel v = new Vokabel("Hund", "dog");

System.out.println(v);

Java ruft bei dieser Ausgabe automatisch die toString()-Methode des Objekts auf. Auf dem Bildschirm erscheint dann:

Hund / dog
Übung 11.1-1 (PC)

Erstellen Sie eine Klasse Vokabel, die ein deutsch-englisches Wortpaar verwaltet. Implementieren Sie die beiden Instanzvariablen, den Konstruktor, die beiden Getter-Methoden sowie die Methode toString().

Testen Sie Ihre Klasse anschließend in BlueJ oder IntelliJ, indem Sie mehrere Vokabel-Objekte erzeugen und deren Methoden aufrufen.

Lösungsvorschlag auf den Seiten für Lehrer(innen) / Nähere Infos dazu

UML-Klassendiagramme, Teil 1

Diejenigen unter Ihnen, die Informatik als schriftliches Abiturfach gewählt haben, müssen sich jetzt mit etwas Neuem beschäftigen. In den Vorgaben zum Informatik-Abitur NRW wird verlangt, dass Sie sich mi UML auskennen.

UML (Unified Modeling Language) ist eine standardisierte Modellierungssprache, mit der man die Strukturen und Zusammenhänge von Softwaresystemen grafisch darstellen kann. UML bietet zahlreiche Möglichkeiten und ist entsprechend umfangreich - im Grunde eine Wissenschaft für sich. Für das Informatik-Abitur und für unsere Klausuren benötigen wir allerdings nur die sogenannten UML-Klassendiagramme, mehr nicht. Im Informatik-Studium sieht das natürlich dann anders aus.

Wenn wir die Klasse Vokabel als UML-Klassendiagramm darstellen wollen, zeichnen wir ein Rechteck und unterteilen es durch waagerechte Linien in drei Abschnitte. Im oberen Abschnitt steht (zentriert) der Name der Klasse. Im mittleren Abschnitt stehen die Instanzvariablen bzw. Attribute der Klasse und im unteren Abschnitt die Methoden. Dabei müssen nicht unbedingt alle Attribute und Methoden aufgeführt werden; je nach Zweck des Diagramms kann man sich auf die wesentlichen beschränken. Für unsere Klasse Vokabel mit ihren beiden Attributen und wenigen Methoden sieht das UML-Klassendiagramm noch ganz einfach aus:

UML-Klassendiagramm der Klasse Vokabel mit zwei privaten Attributen und vier öffentlichen Operationen

Die beiden Attribute vom Typ String heißen deutsch und englisch. Das Minuszeichen vor den beiden Namen bedeutet, dass die Attribute als private deklariert sind. Auf diese Attribute kann von außerhalb der Klasse nicht direkt zugegriffen werden. Ihre Werte können aber über die entsprechenden Getter-Methoden abgefragt werden. Dadurch wird verhindert, dass andere Klassen die Attributwerte unkontrolliert verändern.

Der Konstruktor ist mit einem Pluszeichen gekennzeichnet. Das bedeutet, dass er öffentlich zugänglich ist (public). Auch die übrigen Methoden sind mit einem Pluszeichen versehen, da sie ebenfalls öffentlich zugänglich sind. In UML steht das Zeichen + also für public und das Zeichen - für private.

Die UML-Schreibweise unterscheidet sich deutlich von der Java-Syntax. Das liegt daran, dass UML keine Programmiersprache ist, sondern eine programmiersprachenunabhängige Modellierungssprache (modeling language). Mit UML können wir die Struktur einer Klasse beschreiben, ohne uns auf eine bestimmte Programmiersprache festzulegen. Ein UML-Klassendiagramm kann daher als Grundlage für die Implementierung einer Klasse in verschiedenen objektorientierten Programmiersprachen dienen. In Java würden wir das Attribut deutsch beispielsweise folgendermaßen deklarieren:

private String deutsch;

Im UML-Klassendiagramm sieht die entsprechende Darstellung dagegen so aus:

- deutsch: String

Der Unterschied ist leicht zu erkennen: In Java steht der Datentyp String vor dem Namen der Variablen, in UML dagegen hinter dem Variablennamen, getrennt durch einen Doppelpunkt.

Schritt 2 - Eine Vokabelliste

Wie bereits angedeutet, ist der nächste Schritt die Erzeugung einer Klasse, welche in der Lage ist, viele Vokabeln zu speichern.

Die einfachste Lösung dafür wäre sicherlich ein Objekt-Array, das in der Lage ist, 100 oder 200 Objekte der Klasse Vokabel zu speichern. Mit Objekt-Arrays hatten wir uns aber schon in dem EF-Kurs beschäftigt, in dieser Folge sollen Sie etwas Neues lernen.

Betrachten Sie dazu den folgenden Quelltext:

Beschreibung siehe folgenden Text

Die Klasse Vokabelliste in BlueJ (Screenshot)
Autor: Ulrich Helmich 2026, Lizenz: Public Domain

Schauen wir uns den Quelltext unserer neuen Klasse Vokabelliste einmal genauer an. Gleich in der ersten Zeile finden wir eine Anweisung, die Sie bisher noch nicht kennengelernt haben:

import java.util.ArrayList;

Mit dieser Anweisung teilen wir Java mit, dass wir die Klasse ArrayList verwenden wollen. Diese Klasse gehört zur Java-Standardbibliothek und stellt uns eine ganze Reihe nützlicher Methoden zur Verfügung, mit denen wir Listen verwalten können.

Die Klasse ArrayList

Sie kennen bereits Arrays aus der Stufe EF. Mit einem Array können wir mehrere Werte oder Objekte desselben Datentyps speichern. Allerdings haben Arrays einen entscheidenden Nachteil: Ihre Größe wird beim Erzeugen festgelegt und kann anschließend nicht mehr verändert werden.

Wenn wir beispielsweise ein Array für 100 Vokabeln erzeugen, können wir darin höchstens 100 Vokabel-Objekte speichern. Wollen wir später eine weitere Vokabel hinzufügen, haben wir ein Problem.

Hier kommt die Klasse ArrayList ins Spiel. Eine ArrayList funktioniert in vielerlei Hinsicht ähnlich wie ein gewöhnliches Array. Sie kann mehrere Werte beziehungsweise Objekte verwalten. Der große Unterschied besteht darin, dass eine ArrayList bei Bedarf automatisch wachsen oder auch wieder kleiner werden kann. Wir müssen uns also nicht vorher überlegen, wie viele Vokabeln unsere Liste maximal enthalten soll.

Außerdem stellt uns die Klasse ArrayList zahlreiche praktische Methoden zur Verfügung, mit denen wir Elemente hinzufügen, entfernen, suchen oder auf bestimmte Elemente zugreifen können.

Die spitzen Klammern

Was bedeutet nun das Wort "Vokabel" in den spitzen Klammern?

ArrayList <Vokabel> liste;

Zunächst einmal deklarieren wir hier eine Variable mit dem Namen liste. Diese Variable soll später auf ein Objekt der Klasse ArrayList verweisen.

Aber was bedeutet das Wort Vokabel zwischen den spitzen Klammern?

Ganz einfach: Wir teilen Java damit mit, welche Art von Objekten unsere Liste enthalten soll. In unserem Fall sollen ausschließlich Objekte der Klasse Vokabel gespeichert werden.

Wir könnten beispielsweise auch eine Liste für Strings oder Integer-Objekte deklarieren:

ArrayList <String> namen;
ArrayList <Integer> zahlen;

Die erste Liste kann String-Objekte verwalten, die zweite dagegen Integer-Objekte.

Die Klasse ArrayList ist also nicht auf einen bestimmten Objekttyp festgelegt. Wir können beim Verwenden der Klasse selbst bestimmen, welche Art von Objekten in der Liste gespeichert werden soll.

Solche Klassen bezeichnet man in Java als generische Klassen. Der Begriff generisch bedeutet hier, dass eine Klasse für unterschiedliche Objekttypen verwendet werden kann, ohne dass wir sie jedes Mal neu programmieren müssen.

Ein weiterer Vorteil: Java überprüft bereits beim Übersetzen des Programms, ob wir versuchen, ein Objekt des falschen Typs in unsere Liste einzufügen. Wollten wir also beispielsweise einen String in unsere ArrayList liste einfügen, würde der Compiler eine Fehlermeldung produzieren und den Quelltext nicht übersetzen.

Der Konstruktor

Schauen wir uns jetzt den Konstruktor unserer Klasse Vokabelliste an:

public Vokabelliste()
{
    liste = new ArrayList<>();
}

Hier wird mit dem Schlüsselwort new ein neues Objekt der Klasse ArrayList erzeugt. Die Variable liste erhält eine Referenz auf dieses Objekt. Das heißt, nicht die Objekte selbst mit ihren Instanzvariablen und Methoden befinden sich in der Liste, sondern die Liste enthält Verweise oder Referenzen auf diese Objekte. Aber das kennen Sie ja bereits aus der Stufe EF.

Die Experten unter Ihnen wundern sich jetzt vielleicht über das Paar spitzer Klammern <> hinter ArrayList. Würden wir mit Java, Version 6 arbeiten, müssten wir an dieser Stelle noch schreiben:

liste = new ArrayList<Vokabel>();

Seit der Version Java 7 ist das nicht mehr nötig, der Compiler weiß, dass hier eigentlich "Vokabel" stehen sollte. Die leeren spitzen Klammern <> werden übrigens als Diamant-Operator (Diamond Operator) bezeichnet.

Nach dem Aufruf des Konstruktors besitzen wir eine zunächst leere Liste, in die wir beliebig viele Vokabel-Objekte einfügen können – natürlich nur so viele, wie der verfügbare Arbeitsspeicher zulässt.

Die Methode add()

Kommen wir nun zur letzten Methode unserer Klasse:

public void hinzufuegen(Vokabel v)
{
    liste.add(v);
}

Diese Methode erhält als Parameter eine Referenz auf ein Objekt der Klasse Vokabel. Innerhalb der Methode wird die Methode add() der Klasse ArrayList aufgerufen. Beim Aufruf von add() wird das übergebene Objekt (bzw. die Referenz auf dieses Objekt) an das Ende der Liste eingefügt. Wenn unsere Liste beispielsweise bereits drei Vokabel-Objekte enthält, wird das neue Objekt an der vierten Position eingefügt. Die Liste vergrößert sich dabei gegebenenfalls.

Wichtig ist noch ein weiterer Punkt: Die Methode add() erzeugt kein neues Vokabel-Objekt. Sie fügt lediglich die Referenz auf ein bereits vorhandenes Objekt in die Liste ein.

Schritt 3 - Eine Test-Klasse für die Vokabelliste

Wir könnten natürlich mit BlueJ mehrere Objekte der Klasse Vokabel manuell anlegen und dann in ein neues Objekt der Klasse Vokabelliste einfügen. Dieses Vorgehen ist aber sehr umständlich und auf Dauer auch zeitraubend. Daher schreiben wir uns eine neue Klasse, mit der wir die Klasse Vokabelliste testen können.

public class TesteVokabelliste
{
    Vokabelliste testliste;
    
    public TesteVokabelliste()
    {
        testliste = new Vokabelliste();
        fuegeVokabelnEin();
    }
    
    public void fuegeVokabelnEin()
    {
        testliste.hinzufuegen(new Vokabel("Hund","dog"));
        testliste.hinzufuegen(new Vokabel("Katze","cat"));
        testliste.hinzufuegen(new Vokabel("Esel","donkey"));
        testliste.hinzufuegen(new Vokabel("Kuh","cow"));
        testliste.hinzufuegen(new Vokabel("Schwein","pig"));
    }
}

Zuerst wird ein Objekt der Klasse Vokabelliste als Instanzvariable angelegt.

Im Konstruktor der Testklasse wird dieses Objekt initialisiert, und dann wird eine Methode aufgerufen, mit der fünf Vokabeln in die Liste eingefügt werden.

Untersuchung der Test-Klasse mit dem BlueJ-Objektinspektor

Mit dem BlueJ-Objektinspektor können wir das Zusammenspiel der drei Klassen wunderbar untersuchen. Als erstes erzeugen wir ein Objekt der Testklasse.

Beschreibung siehe folgenden Text

Erzeugung eines Objektes der Klasse TesteVokabelliste
Autor: Ulrich Helmich 2026, Lizenz: Public Domain

Wir führen nun einen Doppelklick auf den roten Objekt-Kasten aus:

Beschreibung siehe folgenden Text

Erzeugung eines Objektes der Klasse TesteVokabelliste
Autor: Ulrich Helmich 2026, Lizenz: Public Domain

Der hintere rote Kasten entsteht, wenn wir auf den roten Kasten für das TesteVokabelliste-Objekt doppelklicken. Dieser Kasten zeigt nur eine Instanzvariable, nämlich das Objekt testliste der Klasse Vokabelliste. Da dieses Objekt eine Referenz ist, wird es durch einen Pfeil dargestellt.

Wir doppelklicken nun auf diesen Pfeil, und ein zweites Fenster des Objektinspektors erscheint. Das Objekt testliste enthält eine Instanzvariable liste, die wiederum ein Objekt der Klasse ArrayList<Vokabel> ist.

Wenn wir nun diesen Pfeil doppelklicken, öffnet sich ein drittes Objektinspektor-Fenster (in der Zeichnung ganz vorne). Hier sehen wir eine Instanzvariable namens elementData. Bei dieser Variable handelt es sich um einen Verweis auf ein Array.

Die Angabe

Object[] elementData

ist so zu lesen: Das Array elementData ist ein Objekt-Array, in ihm können also Objekte der Klasse Object oder Objekte einer Unterklasse von Object gespeichert werden.

Die Besserwisser unter Ihnen wissen es natürlich besser: Es werden nicht die Objekte selbst gespeichert, sondern Verweise oder Referenzen auf diese Objekte.

Wir klicken nun auf den Pfeil in dem vorderen Fenster des Objektinspektor und erhalten folgendes Bild:

Beschreibung siehe folgenden Text

Erzeugung eines Objektes der Klasse TesteVokabelliste
Autor: Ulrich Helmich 2026, Lizenz: Public Domain

Die Instanzvariable elementData ist eine Referenz auf ein "richtiges" Array der Größe 10; es können also 10 Verweise auf Objekte in diesem Array gespeichert werden.

In dem Testprogramm hatten wir fünf Vokabeln in die Vokabelliste aufgenommen (mit der add()-Methode der Klasse ArrayList). Daher sehen wir in dem Array auch fünf Verweise auf konkrete Objekte. Die anderen fünf Arrayelemente verweisen noch nicht auf irgendwelche Objekte, daher sehen wir den Wert null in diesen weißen Kästen.

Schließlich klicken wir auf den ersten und dann auf den zweiten Pfeil in diesem vierten Fenster des Objektinspektors:

Beschreibung siehe folgenden Text

Erzeugung eines Objektes der Klasse TesteVokabelliste
Autor: Ulrich Helmich 2026, Lizenz: Public Domain

Jetzt endlich sehen wir die BlueJ-Darstellung der beiden ersten Objekte der Vokabelliste, das Wortpaar "Hund / dog" sowie das Wortpaar "Katze / cat".

Es geht auch einfacher...

Die Sache mit dem BlueJ-Objektinspektor ist natürlich nicht schlecht, ich selbst kenne keine Entwicklungsumgebung für Java, die einen besseren Einblick in die "Innereien" von Objekten ermöglicht.

Aber etwas umständlich ist es schon, wenn man immer wieder vier oder fünf Doppelklicks ausführen muss, um eines der Objekte in der ArrayList zu sehen.

Was wir benötigen, ist eine Methode in dem Testprogramm, welche die erzeugten fünf Vokabel-Objekte in der Konsole anzeigt.

    public void zeigeAlleVokabeln()
    {
        for (int i = 0; i < 5; i++)
        {
            Vokabel v = testliste.getVokabel(i);
            System.out.println(v);
        }
    }

Diese Methode zeigt alle fünf eingefügten Vokabeln der Liste in der Konsole an. Dazu ruft sie in einer for-Schleife fünfmal die Methode getVokabel() der Klasse Vokabelliste auf, die wir jetzt noch schreiben müssen:

   public Vokabel getVokabel(int i)
    {
        if (i >= 0 && i < liste.size())
        {
            return liste.get(i);
        }
        else
        {
            return null;
        }
    }

Diese Methode ist etwas länger geworden, weil wir zunächst zwei Prüfungen durchführen müssen. Der übergebene Parameter int i steht für den Index der Vokabel in der Vokabelliste. Diese Index darf nicht negativ sein; das erste Element eines Arrays hat stets den Wert 0. Und der Index darf auch nicht größer sein als die Zahl der in der Liste vorhandenen Elemente - 1. Die Methode size() der Klasse ArrayList liefert genau diesen Wert zurück. Wenn wir fünf Elemente in die Liste aufgenommen haben, hat size() auch genau diesen Wert 5. Das letzte Element der ArrayList hat dann den Index 4, also müssen wir fragen: i < liste.size() und nicht etwa i <= liste.size().

Kleine Verbesserung

Etwas unschön ist die Tatsache, dass wir in der for-Schleife der Methode zeigeAlleVokabeln() den Wert 5 manuell hinschreiben müssen. Sobald sich die Anzahl der Vokabeln ändert, müssen wir auch diese Zahl verändern. Eine kleine Verbesserung dieser Methode sieht so aus:

    public void zeigeAlleVokabeln()
    {
        for (int i = 0; i < testliste.getAnzahl(); i++)
        {
            Vokabel v = testliste.getVokabel(i);
            System.out.println(v);
        }
    }

Der Code ist jetzt zwar etwas länger geworden, dafür ist er aber flexibler. Die for-Schleife funktioniert jetzt zuverlässig, egal wie viele Elemente in der Arrayliste enthalten sind.

Damit diese neue Variante von zeigeAlleVokabeln() auch funktioniert, müssen wir die Klasse Vokabelliste noch um eine Getter-Methode ergänzen, welche diese Anzahl auch zurückliefert:

    public int getAnzahl()
    {
       return liste.size();
    }

Dazu greife Vokabelliste.getAnzahl() einfach auf ArrayList.size() zurück.

➥Für Abiturienten und Studenten: UML-Diagramme

Die Abiturienten und Studenten unter Ihnen sollten sich auf jeden Fall den Exkurs über UML-Klassendiagramme ansehen. Im NRW-Informatikabitur werden Kenntnisse über solche Klassendiagramme auf jeden Fall verlangt.

Seitenanfang -
Teil 2 dieser Folge...