Java-Klassen und Objekte

Lerne, eigene Klassen in Java zu schreiben. Definiere die Felder, die den Zustand eines Objekts speichern, schreibe Konstruktoren, die jedem neuen Objekt gültige Anfangswerte geben, und ergänze Instanzmethoden, die diesen Zustand verwenden und ändern. Schütze deine Objekte mit privaten Feldern, Gettern, Settern und Prüfungen, gib ihnen eine lesbare toString-Methode und entwirf, implementiere und teste zum Abschluss eine vollständige Klasse anhand einer kurzen Beschreibung.

AnfängerEinheit 5 von 11 in Kurs: Java-Programmierung – Klassen, Vererbung, Collections und Streams

Was du lernst

  1. Erkläre, wie eine Klasse einen neuen Datentyp mit eigenem Verhalten definiert, und erzeuge und verwende Objekte dieses Typs in einem Programm.
    • Erkläre, was eine Klasse und ein Objekt sind
      Beschreibe eine Klasse als Definition eines Typs, ein Objekt als eine Instanz dieses Typs sowie Zustand und Verhalten als das, was Objekte speichern und tun.
    • Erzeuge Objekte und rufe ihre Methoden auf
      Erzeuge Objekte mit new, rufe ihre Methoden mit Punktnotation auf und sage voraus, wie sich verschiedene Objekte verhalten.
  2. Deklariere die Instanzvariablen, die den Zustand eines Objekts speichern, und schreibe Konstruktoren, die jedem neuen Objekt gültige Anfangswerte geben.
    • Deklariere Instanzvariablen
      Deklariere Felder mit passenden Typen, kenne ihre Standardwerte und unterscheide sie von lokalen Variablen und Parametern.
    • Schreibe Konstruktoren, die Objekte initialisieren
      Schreibe Konstruktoren, die jedes Feld setzen, verwende this, um Felder von Parametern zu unterscheiden, und vermeide Fehler, durch die Felder nicht gesetzt werden.
    • Stelle mehrere Konstruktoren bereit und verkette sie
      Überlade Konstruktoren für verschiedene Arten, ein Objekt zu erzeugen, und verwende this(...), damit der Initialisierungscode nur einmal geschrieben wird.
  3. Schreibe Instanzmethoden, die den Zustand eines Objekts lesen und ändern, verfolge ihre Wirkung und entscheide, wann etwas zu einem Objekt oder zur Klasse als Ganzes gehört.
    • Schreibe Instanzmethoden, die den Zustand verwenden und ändern
      Schreibe Methoden, die Informationen über die Felder eines Objekts liefern oder sie ändern, rufe andere Methoden desselben Objekts auf und verfolge den Zustand von Objekten Schritt für Schritt.
    • Unterscheide Instanzmitglieder von statischen Mitgliedern
      Verwende this für das aktuelle Objekt, entscheide, was statisch sein sollte, und behebe Fehler, die durch das Vermischen von statischem und Instanzcode entstehen.
  4. Verberge Felder mithilfe von Zugriffsmodifikatoren, ermögliche kontrollierten Zugriff über Getter, Setter und aussagekräftige Methoden und halte jedes Objekt in einem gültigen Zustand.
    • Verberge Daten mit Zugriffsmodifikatoren
      Verwende private, Paketzugriff, protected und public, um zu steuern, welcher Code die Mitglieder einer Klasse verwenden kann, und erkläre, warum Felder privat gehalten werden.
    • Schreibe Getter und Setter
      Schreibe Accessor- und Mutator-Methoden mit den üblichen Namen und entscheide, welche Felder sie brauchen und wann eine aussagekräftige Methode besser ist.
    • Sichere gültige Objektzustände durch Prüfungen
      Definiere die Regeln für den Zustand eines Objekts, setze sie in Konstruktoren und Methoden durch und vermeide die Weitergabe interner Daten, mit der sich diese Prüfungen umgehen lassen.
  5. Gib Objekten mit toString eine lesbare Textform und entwirf, schreibe und teste anschließend eine vollständige Klasse anhand einer kurzen Beschreibung.
    • Schreibe eine toString-Methode
      Schreibe eine toString-Methode, die eine lesbare Beschreibung eines Objekts zurückgibt, und lerne, wann Java sie automatisch aufruft.
    • Entwirf, implementiere und teste eine vollständige Klasse
      Setze eine kurze Beschreibung in eine Klasse mit Feldern, Konstruktoren, Methoden und Regeln um und teste anschließend jedes Verhalten von einem Hauptprogramm aus.

Java-Klassen und Objekte einfach erklärt

5 Kapitel

1

Klassen, Objekte und Methodenaufrufe

Ein Programm muss oft mehrere zusammengehörige Werte zusammenhalten, zum Beispiel Name, Tierart und Alter eines Haustiers. Eine Klasse gibt dieser Gruppe einen Typ und bündelt die Aktionen, die sie verwenden, an einer Stelle. Du wirst Klassendefinitionen mit den Objekten verbinden, die während der Programmausführung entstehen, und anschließend Methodenaufrufe verfolgen, um zu sehen, welche Objektdaten sich ändern. Die Unterscheidung zwischen Klasse, Objekt und Variable hilft dir, den Code zu lesen, den du als Nächstes schreibst.

1.1 Ein Typ mit Zustand und Verhalten

Eine Klasse definiert einen Typ: die Daten, die sich seine Objekte merken, und die Operationen, die sie anbieten. Ein Objekt, auch Instanz genannt, ist ein Wert dieses Typs, der während der Programmausführung erzeugt wird. Das Schreiben einer Klassendefinition erzeugt kein Objekt.

Zustand und Verhalten

Name, Tierart und Alter eines Haustiers bilden seinen Zustand. Diese aktuellen Werte stehen in Instanzvariablen, die auch Felder genannt werden. Eine Operation wie das Feiern eines Geburtstags ist Verhalten und wird als Methode geschrieben. Die Geburtstagsmethode gehört zu dem Alter, das sie ändert, sodass die Regel an einer Stelle steht.

Stell dir eine Klasse als Bauplan vor. Viele Häuser können nach einem Plan entstehen, aber jedes Haus hat seinen eigenen Inhalt. Ebenso können zwei Haustiere derselben Definition folgen und unterschiedlich alt sein. Du schreibst die Feldnamen einmal; jedes Objekt erhält eigene Werte.

Lies eine kleine Klasse
public class Pet {
    private String name;
    private String species;
    private int age;

    public Pet(String name, String species, int age) {
        this.name = name;
        this.species = species;
        this.age = age;
    }

    public void celebrateBirthday() {
        this.age++;
    }
}

Lies dies von außen nach innen. public class Pet führt den Typ ein und die äußeren geschweiften Klammern enthalten seine Definition. Die ersten drei Deklarationen beschreiben den Zustand. Der Block namens Pet ist ein Konstruktor: Er gibt einem neuen Objekt seine ersten Werte. celebrateBirthday ist eine Methode: Sie erhöht das Alter des aktuellen Objekts. Die Wörter private und public steuern den Zugriff; spätere Beispiele verwenden sie, um den Zustand zu schützen.

Die Parameter des Konstruktors sind Werte, die beim Erzeugen eines Objekts übergeben werden. In this.age = age; bezeichnet die linke Seite das Feld des Objekts und die rechte Seite den übergebenen Parameter. Der Konstruktor speichert diese Eingabe, damit sich das Objekt nach dem Ende der Konstruktion daran erinnern kann.

Eine Klasse einsetzen

Du hast bereits Bibliotheksklassen verwendet. Scanner beschreibt Objekte, die Eingaben lesen, ArrayList beschreibt Listen und ein String-Objekt bietet Operationen wie length() an. Eine selbst geschriebene Klasse folgt derselben Grundidee: Sie stellt dem Rest deines Programms einen nützlichen Typ bereit.

Ohne Pet müsste ein Programm Name, Tierart und Alter möglicherweise getrennt an viele Methoden übergeben. Es muss jede Gruppe korrekt zusammenhalten. Ein Pet-Objekt trägt die zusammengehörigen Werte als eine Einheit. Methoden auf diesem Objekt wissen, welches Alter sie verwenden sollen.

Code organisieren und benennen

Bei gewöhnlichen Quelldateien wird eine öffentliche Klasse namens Pet in Pet.java gespeichert, wobei die Groß- und Kleinschreibung übereinstimmen muss. Eine separate Klasse Main kann die main-Methode des Programms enthalten, Haustiere erzeugen und ihre Methoden aufrufen. Pet braucht keine eigene main-Methode. Klassen im selben unbenannten Paket können einander ohne Importdeklarationen verwenden; die einfachen Beispiele halten diese Klassen in einem Ordner.

Verwende für Klassen Substantive in UpperCamelCase, etwa LibraryBook. Verwende lowerCamelCase für Felder und Methoden: pageCount bezeichnet Daten; giveBack() bezeichnet eine Aktion. Diese Konventionen helfen beim Lesen, die einzelnen Teile zu erkennen, obwohl der Compiler viele andere Namen erlaubt.

Wichtige Erkenntnisse

  • Eine Klasse definiert einen Typ; ein Objekt ist eine Instanz, die während der Programmausführung erzeugt wird.
  • Felder speichern den Zustand und Methoden stellen das Verhalten bereit.
  • Objekte einer Klasse folgen derselben Definition und speichern ihre eigenen Werte in Instanzfeldern.
  • Eine öffentliche Klasse in einer gewöhnlichen Quelldatei verwendet den passenden Klassennamen als Dateinamen.

1.2 Neue Objekte und ihre Referenzen

Ein Objekt zu erzeugen und eine Variable zu deklarieren sind verschiedene Vorgänge. Eine Variable gibt dir einen Namen, über den du auf ein Objekt zugreifen kannst; new erzeugt das Objekt, auf das der Name verweisen wird.

Lies eine Anweisung zur Objekterzeugung
Pet luna = new Pet("Luna", "cat", 3);

Pet ist der Typ der Variable und luna ihr Name. new Pet(...) erzeugt ein neues Objekt und ruft einen passenden Konstruktor mit drei Argumenten auf. Der Konstruktor speichert Name, Tierart und Alter. Die entstehende Referenz wird luna zugewiesen.

Eine Referenz sagt Java, welches Objekt es verwenden soll. Es ist hilfreich, eine Variable als Namen mit einem Pfeil zu einem Objekt zu zeichnen. Die Variable ist keine zweite Kopie aller Felder des Objekts.

Rufe eine Methode auf einem bestimmten Objekt auf

Der Punkt in luna.celebrateBirthday() verbindet eine Referenz mit einer Methode, die auf ihrem Objekt ausgeführt wird. Eine Methode mit Rückgabewert kann einen Wert für einen anderen Ausdruck liefern:

luna.celebrateBirthday();
int years = luna.getAge();
System.out.println(years); // 4

Hier ist getAge() eine öffentliche Methode, die das Alter des Haustiers zurückgibt. Der Geburtstagsaufruf ändert den Zustand; der Getter liest ihn. Pet.celebrateBirthday() schlägt fehl, weil diese Instanzmethode ein bestimmtes Haustier braucht. Der Klassenname allein beantwortet nicht, wessen Geburtstag es ist.

Getrennte Objekte und gemeinsam genutzte Referenzen
Pet max = new Pet("Max", "dog", 5);
luna.celebrateBirthday();
System.out.println(luna.getAge()); // 5
System.out.println(max.getAge());  // 5

Luna war bereits 4 Jahre alt, also wird sie beim nächsten Geburtstag 5. Max bleibt 5. Das gleiche Alter macht sie nicht zu demselben Objekt: Die beiden new-Ausdrücke haben getrennte Haustiere erzeugt.

Betrachte nun eine Zuweisung ohne new:

Pet other = luna;
other.celebrateBirthday();
System.out.println(luna.getAge()); // 6

Die Zuweisung kopiert die Referenz auf Luna. Beide Namen verweisen auf ein Haustier, sodass eine Änderung über other auch über luna sichtbar ist. Um ein zweites Haustier zu erzeugen, rufe new erneut auf.

Achte auf die Parameter des Konstruktors

Die Argumente müssen in Anzahl, Reihenfolge und Typ zu einem deklarierten Konstruktor passen. Nimmt der einzige verfügbare Konstruktor (String, String, int) entgegen, wird new Pet() nicht kompiliert. Java errät keine fehlenden Werte. Werden die beiden Strings vertauscht, wird der Code dagegen trotzdem kompiliert: new Pet("cat", "Luna", 3) liefert die richtigen Typen, aber die falschen Bedeutungen. Lies die Parameternamen, um die Reihenfolge zu prüfen.

Diese beiden unvollständigen Versuche scheitern aus unterschiedlichen Gründen:

Pet a;                         // declaration only
// System.out.println(a.getAge()); // a has no assigned value
// Pet b = Pet("Bo", "cat", 2);    // missing new

Einer lokalen Variable muss vor ihrer Verwendung ein Wert zugewiesen werden. Ohne new sucht Java nach einer Methode namens Pet, nicht nach einem Ausdruck zur Objekterzeugung. Ist ein Objekt vorhanden, kann seine Ausgabe Text wie Pet@8bcc55f zeigen, bis du eine lesbare toString()-Methode bereitstellst; diese Ausgabe bedeutet nicht, dass die Konstruktion fehlgeschlagen ist.

Wichtige Erkenntnisse

  • Verwende new mit Argumenten, die zu einem verfügbaren Konstruktor passen.
  • Die Punktnotation ruft eine Instanzmethode auf dem Objekt auf, auf das eine Referenz verweist.
  • Ein weiterer new-Ausdruck erzeugt ein weiteres Objekt; eine Referenzzuweisung macht ein vorhandenes Objekt gemeinsam nutzbar.
  • Argumente desselben Typs können ohne Kompilierfehler vertauscht werden. Prüfe deshalb ihre Bedeutung.
  • Eine lokale Deklaration allein erzeugt kein Objekt und gibt der Variable keinen verwendbaren Wert.
2

Felder und der Anfangszustand eines Objekts

Objekte brauchen einen Ort für ihre Daten und eine zuverlässige Möglichkeit, ihre ersten Werte zu erhalten. Felder stellen den Speicher bereit; Konstruktoren initialisieren jedes neue Objekt. Du wählst Feldtypen, trennst dauerhaften Zustand von vorübergehenden lokalen Arbeiten und verfolgst die Konstruktion von den Standardwerten bis zum fertigen Objekt. Anschließend bietest du mehrere sinnvolle Möglichkeiten an, dieselbe Art von Objekt zu erzeugen, und hältst dabei gemeinsame Initialisierung und Prüfungen an einer Stelle.

2.1 Daten, die sich ein Objekt merkt

Ein Objekt braucht für jeden Wert, den es sich zwischen Methodenaufrufen merken muss, ein Feld. Wähle diese Werte, bevor du Methoden schreibst: Sie bilden den dauerhaften Zustand des Objekts.

Deklariere Felder mit passenden Typen

Ein Feld steht innerhalb der geschweiften Klammern der Klasse, aber außerhalb aller Konstruktoren und Methoden. Eine Deklaration braucht einen Typ und einen Namen; ein Zugriffsmodifikator und ein Initialisierer können ebenfalls vorkommen.

private String title;
private int pageCount;
private boolean lentOut = false;

String eignet sich für Text, int für eine ganzzahlige Anzahl und boolean für eine Ja-Nein-Bedingung. Felder können auch Klassentypen wie ArrayList<String> verwenden. Diese Beispiele verwenden private; lässt du den Modifikator weg, gilt Paketzugriff und kein privater Zugriff.

Jedes Objekt erhält eine eigene Kopie jedes Instanzfeldes. Zwei Rectangle-Objekte haben jeweils ein width- und ein height-Feld, obwohl die Deklarationen nur einmal in der Klasse stehen.

Standardwerte sind nicht immer brauchbare Anfangswerte
FeldtypStandardwert
int, long0
double0.0
booleanfalse
Klassentypen, darunter String und ArrayListnull

Ein Feld erhält einen Standardwert, wenn Speicherplatz für das Objekt angelegt wird. Er muss nicht zum Zweck des Objekts passen. Ein Listenfeld mit dem Wert null ist keine leere Liste: Du musst die Liste erst erzeugen, bevor du ein Element hinzufügen kannst.

Lokale Variablen erhalten keinen solchen Standardwert. Innerhalb einer Methode wird int total; gefolgt von System.out.println(total); nicht kompiliert, weil total kein Wert zugewiesen wurde. Halte die Regel für Felder und die für lokale Variablen auseinander.

Entscheide, was bestehen bleiben muss
Art der VariableWo sie deklariert wirdWofür sie dient
InstanzfeldIn der Klasse, außerhalb von MethodenWerte, die sich ein Objekt merkt
ParameterIm Kopf eines Konstruktors oder einer MethodeEingaben für diesen Aufruf
Lokale VariableInnerhalb eines Methoden- oder KonstruktorblocksVorübergehende Arbeit für diesen Aufruf

Ein Feld bleibt bei seinem Objekt. Ein Parameter ist während seines Aufrufs verfügbar und eine lokale Variable in ihrem Block, während dieser Aufruf ausgeführt wird. Muss ein Zähler seinen Zählerstand von einem Aufruf zum nächsten behalten, gehört der Stand in ein Feld. Eine vorübergehende Zwischensumme für eine Berechnung kann lokal sein.

Speichere die Eingaben; berechne abgeleitete Werte
public class Rectangle {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    public double area() {
        return this.width * this.height;
    }
}

Die Abmessungen sind in Zentimetern angegeben, daher gibt area() eine Fläche in Quadratzentimetern zurück. Ein separates Feld für die Fläche würde einen weiteren Wert schaffen, der bei jeder Änderung einer Abmessung aktualisiert werden müsste. Die Berechnung aus den Abmessungen hält das Ergebnis konsistent.

Verwende ein final-Instanzfeld für einen Wert, der einmal zugewiesen wird, etwa private final String title;. Gib ihm einen Wert in seiner Deklaration oder in jedem Konstruktor. final verhindert eine Neuzuweisung. static hat eine andere Bedeutung: Es erzeugt ein gemeinsam genutztes Klassenfeld. Würde der Name eines Haustiers static erhalten, würden verschiedene Haustiere eine Namensvariable gemeinsam nutzen.

Wichtige Erkenntnisse

  • Instanzfelder werden außerhalb von Methoden deklariert und speichern den Zustand jedes Objekts.
  • Felder erhalten Standardwerte; lokalen Variablen muss vor der Verwendung ein Wert zugewiesen werden.
  • Wähle Felder für dauerhafte Daten und lokale Variablen für vorübergehende Arbeit.
  • Berechne Werte, die sich aus gespeicherten Feldern ergeben, um inkonsistente Kopien zu vermeiden.
  • Ein final-Instanzfeld erhält einmal einen Wert; ein statisches Feld wird von der Klasse gemeinsam genutzt.
  • Ein Listenfeld mit null braucht noch ein Listenobjekt, bevor es verwendet werden kann.

2.2 Von Argumenten zu initialisierten Feldern

Ein Konstruktor initialisiert ein Objekt, wenn new es erzeugt. Sein Name muss mit dem der Klasse übereinstimmen und er hat keinen Rückgabetyp. Selbst void würde die Deklaration zu einer Methode machen.

Übertrage Eingaben in den Zustand
public Pet(String name, String species, int age) {
    this.name = name;
    this.species = species;
    this.age = age;
}

Ein Konstruktor nimmt üblicherweise Eingaben als Parameter entgegen und speichert sie in Feldern. Hier hat jeder Parameter denselben Namen wie sein Feld. Der bloße Name age bezeichnet den Parameter; this.age bezeichnet das Feld des Objekts, das gerade erzeugt wird. Die Zuweisung liest rechts den übergebenen Wert und schreibt links in das Feld.

Bei new Pet("Luna", "cat", 3) erhalten die Felder des neuen Objekts zuerst Standardwerte: null, null und 0. Der Konstruktor erhält dann die drei Argumente und ersetzt diese Standardwerte. Wenn die Konstruktion endet, liefert new die Referenz, die der Aufrufer speichert.

Zwei Fehler, die kompiliert werden
public Pet(String name, String species, int age) {
    name = name;
    this.species = species;
    this.age = age;
}

Beide Vorkommen von name beziehen sich auf den Parameter, sodass die erste Zeile diesen Parameter sich selbst zuweist. Der Name des Objekts bleibt null. Der Code ist zulässig, weshalb es wichtig ist, die Werte des neuen Objekts zu prüfen.

Eine Deklaration innerhalb des Konstruktors verursacht ein verwandtes Problem:

String nick = initialNick;

Existiert bereits ein Feld namens nick, erzeugt diese Zeile eine lokale Variable mit demselben Namen. Die lokale Variable verschwindet, wenn die Konstruktion endet. Schreibe this.nick = initialNick;, um die Eingabe im Feld zu speichern; wiederhole den Typ nicht.

Erkenne, wann es einen parameterlosen Konstruktor gibt

Deklariert eine Klasse keine Konstruktoren, stellt Java einen Standardkonstruktor ohne Parameter bereit. Bei einer einfachen Klasse behalten die Felder ihre deklarierten Anfangswerte oder Standardwerte. Sobald du irgendeinen Konstruktor deklarierst, stellt Java diesen Standardkonstruktor nicht mehr bereit.

Eine Klasse mit nur Pet(String name, String species, int age) kann daher nicht mit new Pet() erzeugt werden. Ergänze einen ausdrücklichen parameterlosen Konstruktor nur, wenn es dafür einen sinnvollen Anfangszustand gibt. Achte auch auf public void Pet(...): Das ist eine Methode und stellt den beabsichtigten Konstruktor daher überhaupt nicht bereit.

Setze auch interne Anfangswerte

Nicht jedes Feld muss aus einem Argument stammen. Ein neues Buch kann immer mit this.lentOut = false; beginnen. Eine Klasse, die eine Liste besitzt, kann sie bei importiertem ArrayList so initialisieren:

private ArrayList<String> tracks;

public Playlist() {
    this.tracks = new ArrayList<>();
}

Die leere Liste ist für add bereit. Bleibt tracks auf null, schlägt ein späteres tracks.add(...) mit einer NullPointerException fehl.

Ein Feldinitialisierer wird vor dem Konstruktorrumpf ausgeführt. Folgt auf private int score = 10; im Konstruktor this.score = 5;, ist der Wert von score im fertigen Objekt 5. Prüfe die Werte nach der Konstruktion und nicht nur die Werte neben den Felddeklarationen. Konstruktoren brauchen außerdem dieselben Validierungsregeln wie spätere Änderungen, damit der erste Zustand gültig ist.

Wichtige Erkenntnisse

  • Ein Konstruktor hat den Namen der Klasse und keinen Rückgabetyp.
  • Verwende this.field = parameter, wenn die Namen übereinstimmen.
  • Weder die Selbstzuweisung eines Parameters noch eine neue lokale Variable initialisiert das beabsichtigte Feld.
  • Java stellt nur dann einen Standardkonstruktor bereit, wenn die Klasse keine Konstruktoren deklariert.
  • Initialisiere internen Zustand wie leere Listen und wende die Validierung bei der Konstruktion an.

2.3 Mehrere Wege, eine Art von Objekt zu konstruieren

Eine Klasse kann mehrere sinnvolle Möglichkeiten anbieten, ein Objekt zu erzeugen. Überladen von Konstruktoren bedeutet, Konstruktoren mit unterschiedlichen Parameterlisten zu deklarieren.

Unterscheide Konstruktoren anhand ihrer Parameter

Java unterscheidet Konstruktoren anhand von Anzahl, Typen und Reihenfolge ihrer Parameter. Die Parameternamen unterscheiden sie nicht. Box(String label) und Box(String name) beschreiben dieselbe Parameterliste und können daher nicht beide in einer Klasse deklariert werden.

Ein Haustier kann mit einer Altersangabe oder als Neugeborenes erzeugt werden:

public Pet(String name, String species, int age) {
    this.name = name;
    this.species = species;
    this.age = age;
}

public Pet(String name, String species) {
    this(name, species, 0);
}

new Pet("Pip", "rabbit") wählt den Konstruktor mit zwei Parametern. Dieser Konstruktor setzt 0 als fehlendes Alter ein und ruft den Konstruktor mit drei Parametern auf. Das neue Objekt hat am Ende den Zustand "Pip", "rabbit" und 0.

Verknüpfe die Initialisierung desselben Objekts

Innerhalb eines Konstruktors ruft this(...) einen anderen Konstruktor derselben Klasse auf. Der kürzere Konstruktor wählt Standardwerte; der umfassendere Konstruktor übernimmt die gemeinsame Initialisierung. Prüfungen in dieser Initialisierung gelten für beide Konstruktionswege.

Vergleiche zwei ähnlich aussehende Zeilen:

Zeile innerhalb eines KonstruktorsWirkung
this(name, species, 0);Initialisiert das aktuelle Objekt über einen anderen Konstruktor
new Pet(name, species, 0);Erzeugt ein separates Haustier

Die zweite Zeile füllt nicht die Felder des aktuellen Objekts. Wird ihr Ergebnis nie gespeichert, wird das zusätzliche Haustier verworfen. Die Felder des aktuellen Haustiers können weiterhin null enthalten.

Verfolge eine Kette, bevor du späteren Code nachvollziehst

Eine Uhr kann Mitternacht als Standard anbieten:

public Clock() {
    this(0, 0);
}

public Clock(int hours, int minutes) {
    this.hours = hours;
    this.minutes = minutes;
}

Gehe bei new Clock() in den parameterlosen Konstruktor, folge this(0, 0) und weise beide Felder im Konstruktor mit zwei Parametern zu. Danach kehrt die Ausführung zum parameterlosen Konstruktor zurück. So entsteht eine Uhr, nicht zwei. Ein Aufruf von new Clock(9, 5) beginnt direkt im umfassenderen Konstruktor.

Halte die Kette zulässig und verständlich

Ein Konstruktor kann höchstens einen this(...)-Aufruf enthalten. Die Kette muss schließlich eine Initialisierung erreichen, die nicht in die Kette zurückruft: Zwei Konstruktoren, die aneinander delegieren, verursachen beim Kompilieren einen Fehler wegen eines rekursiven Konstruktoraufrufs.

this(...) an den Anfang zu setzen, ist ein klarer Stil, der über Java-Versionen hinweg funktioniert. In Java 24 und früher muss der Aufruf ohne Vorschaufunktionen an erster Stelle stehen. Java 25 erlaubt vorherige Anweisungen für Aufgaben wie das Berechnen oder Prüfen von Argumenten. Diese Anweisungen dürfen weder Instanzfelder des aktuellen Objekts lesen noch dessen Instanzmethoden aufrufen; Zuweisungen an Felder, die in der Klasse ohne Initialisierer in der Deklaration deklariert wurden, sind erlaubt.

Biete Überladungen an, weil Aufrufer unterschiedliche sinnvolle Eingaben haben. Eine Uhr auf Mitternacht einzustellen ist sinnvoll; ein Buch ohne Titel muss es nicht sein. Ein vollständiger Konstruktor erzeugt nicht automatisch kürzere Überladungen.

Wichtige Erkenntnisse

  • Überladene Konstruktoren unterscheiden sich durch ihre Parameterlisten, nicht durch die Parameternamen.
  • Der Aufruf this(...) delegiert an einen anderen Konstruktor für dasselbe Objekt.
  • Halte gemeinsame Initialisierung und Prüfungen in einem Konstruktionsweg.
  • Eine Konstruktorkette darf nicht zu einem Konstruktor zurückführen, der bereits in dieser Kette steht.
3

Methoden, die den Zustand lesen und ändern

Ein nützliches Objekt kann Fragen über sich beantworten und Aktionen ausführen, die seinen Zustand ändern. Instanzmethoden stellen diese Operationen neben die Felder, die sie verwenden. Du verfolgst Änderungen eine Anweisung nach der anderen, behandelst den Überlauf einer Uhr und gibst Werte zurück, die anderer Code verwenden kann. Außerdem entscheidest du, welche Daten zu jedem einzelnen Objekt und welche Daten oder Operationen zur Klasse gehören, damit getrennte Objekte getrennt bleiben.

3.1 Berechnungen, Änderungen und Zustandsprotokolle

Eine Instanzmethode wird für ein bestimmtes Objekt ausgeführt. Deklariere sie ohne static und verwende Felder dieses Objekts über Namen wie this.width. Entscheide, was der Aufrufer braucht: eine Zustandsänderung, ein zurückgegebenes Ergebnis oder beides.

Gib Berechnungen zurück; mache Zustandsänderungen ausdrücklich

Für ein Rechteck, dessen Abmessungen in Zentimetern gespeichert sind:

public double area() {
    return this.width * this.height;
}

public double perimeter() {
    return 2 * (this.width + this.height);
}

public void scale(double factor) {
    this.width = this.width * factor;
    this.height = this.height * factor;
}

Ein Rechteck mit den Seiten 3,0 und 4,0 hat eine Fläche von 12,0 Quadratzentimetern und einen Umfang von 14,0 Zentimetern. scale(2) ändert die Seiten auf 6,0 und 8,0. Die nächsten Aufrufe geben die Fläche 48,0 und den Umfang 28,0 zurück. Jede Methode liest die aktuellen Felder, sodass keine gespeicherte Fläche aktualisiert werden muss.

area() gibt eine Zahl zurück, die gespeichert oder in einer Berechnung verwendet werden kann. Würde die Methode die Zahl nur ausgeben und den Rückgabetyp void haben, könnte der Aufrufer nicht double twice = r.area() * 2; schreiben. Überlasse die Ausgabe dem Aufrufer, wenn eine Methode einen Wert berechnen soll.

Aktualisiere zusammengehörige Felder der Reihe nach

Die Methode advance() einer Uhr muss sowohl den Minuten- als auch den Stundenüberlauf behandeln:

public void advance() {
    this.minutes++;
    if (this.minutes == 60) {
        this.minutes = 0;
        this.hours++;
        if (this.hours == 24) {
            this.hours = 0;
        }
    }
}

Bei 23:58 Uhr erreicht der erste Aufruf 23:59 Uhr und überspringt beide Blöcke zum Zurücksetzen. Der nächste Aufruf erreicht kurz Minute 60, setzt sie auf 0 zurück, erhöht die Stunde auf 24 und setzt auch diese auf 0 zurück. Der endgültige Zustand ist 00:00 Uhr. Ein weiterer Aufruf ergibt 00:01 Uhr.

Verfolge Objekte getrennt

Schreibe nach jeder Anweisung eine Zeile und für jedes Feld jedes Objekts eine Spalte. Ändere nur das Objekt, auf dem die Methode aufgerufen wird. Bei Haustieren im Alter von 2 und 7 Jahren ergeben zwei Geburtstage beim ersten Haustier und setAge(1) beim zweiten die Alterswerte 4 und 1. Führe den Rumpf einer Methode nicht für jede Instanz aus, nur weil sie derselben Klasse angehören.

Eine lokale Deklaration kann das Feld verdecken, das du aktualisieren wolltest. public void reset() { int count = 0; } erzeugt nur eine temporäre Variable. this.count = 0; setzt den dauerhaft gespeicherten Zählerstand zurück.

Verwende Methoden wieder und vervollständige Rückgabepfade

Innerhalb einer Instanzmethode rufen area() und this.area() die Flächenmethode desselben Objekts auf. Eine Methode zur Beschreibung kann diese Berechnung wiederverwenden, statt ihren Ausdruck zu kopieren. Eine Methode kann auch den Zustand ändern und einen Erfolg zurückgeben, wie lend(), wenn sie ein Buch als ausgeliehen markiert und true meldet.

Eine Methode mit einem anderen Rückgabetyp als void darf nicht einfach das Ende erreichen, ohne einen kompatiblen Wert zurückzugeben. Wenn get() nur innerhalb von if (count > 0) zurückkehrt, was geschieht dann, wenn der Zählerstand 0 ist? Ergänze den nötigen Rückgabepfad oder wirf für einen Wert, den die Methode ablehnt, eine Ausnahme. Ein Pfad, der eine Ausnahme wirft, kehrt nicht zum Aufrufer zurück und braucht keinen Rückgabewert.

Wichtige Erkenntnisse

  • Instanzmethoden lesen oder ändern den Zustand des Objekts, auf dem sie aufgerufen werden.
  • Gib berechnete Werte zurück, damit Aufrufer sie in Ausdrücken verwenden können.
  • Verfolge Anweisungen der Reihe nach und halte die Felder jedes Objekts auseinander.
  • Methoden zur Behandlung eines Überlaufs müssen alle zugehörigen Felder in ihren erlaubten Bereich zurücksetzen.
  • Verwende andere Methoden desselben Objekts wieder, indem du sie direkt oder mit this aufrufst.
  • Eine Methode mit Rückgabewert braucht auf jedem Pfad, der regulär zurückkehrt, einen Wert.

3.2 Objektbezogene und gemeinsam genutzte Klassenmitglieder

Frag dich, zu wem ein Wert oder eine Operation gehört, bevor du dich für oder gegen static entscheidest. Das Alter eines Haustiers gehört zu einem Haustier. Die Anzahl aller erzeugten Haustiere gehört zur Klasse als Ganzes.

Das aktuelle Objekt ist this

Innerhalb eines Konstruktors oder einer Instanzmethode verweist this auf das Objekt, das gerade konstruiert wird oder den Aufruf empfängt. In this.age++ bezeichnet es, wessen Alter sich ändert. In this.name = name unterscheidet es das Feld von einem gleichnamigen Parameter. this(...) ist die Form zur Konstruktorverkettung.

Eine statische Methode hat kein aktuelles Objekt, daher kann this dort nicht verwendet werden. Sie kann dennoch ein Objekt erzeugen oder eine Referenz erhalten und damit Instanzmethoden aufrufen.

Gemeinsam genutzte und objektbezogene Felder
public class Counter {
    private int count;
    private static int created = 0;

    public Counter() {
        this.count = 0;
        created++;
    }

    public void increment() {
        this.count++;
    }

    public int getCount() {
        return this.count;
    }

    public static int getCreated() {
        return created;
    }
}

Nach zwei Aufrufen von new Counter() gibt es zwei getrennte count-Felder, die beide anfangs 0 sind, und ein created-Feld mit dem Wert 2. Erhöhst du den ersten Zähler, ändert sich sein Zählerstand auf 1; der zweite bleibt bei 0. Counter.getCreated() gibt weiterhin 2 zurück, weil kein neues Objekt konstruiert wurde.

Arbeite von main aus über ein Objekt
public static void main(String[] args) {
    Counter c = new Counter();
    c.increment();
    System.out.println(c.getCount());
}

Die klassische main-Methode ist statisch. Ein bloßer Aufruf von increment() hätte dort kein Zielobjekt und würde nicht kompiliert. Die Variable c liefert dieses Ziel. Würdest du count statisch machen, um den Fehler zu beseitigen, würde sich der Entwurf ändern: Jeder Zähler würde dann einen Zählerstand gemeinsam nutzen.

Art der MethodeVerfügbarer direkter Zugriff
InstanzmethodeIhre Instanzmitglieder und die statischen Mitglieder der Klasse
Statische MethodeStatische Mitglieder; Instanzmitglieder über eine Objektreferenz

Java 25 unterstützt auch Instanzformen von main, aber der klassische statische Methodenkopf bleibt gültig. Diese Beispiele verwenden ihn durchgehend.

Wähle Methoden und Konstanten bewusst

Eine Operation, die die Felder eines Objekts braucht, sollte eine Instanzmethode sein. Eine Operation, die nur von ihren Parametern abhängt, kann statisch sein, etwa eine Hilfsmethode, die eine Gesamtzahl von Minuten in Stunden umrechnet. Statische Aufrufe verwenden normalerweise den Klassennamen: Math.sqrt(16) oder Counter.getCreated().

Ein gemeinsamer fester Wert wird üblicherweise als Konstante deklariert:

public static final int MAX_CELSIUS = 30;

public macht sie für Aufrufer verfügbar, static gibt der Klasse ein gemeinsam genutztes Feld und final verhindert eine Neuzuweisung. UPPER_SNAKE_CASE kennzeichnet nach Konvention eine Konstante. Dies ist eine häufige Ausnahme von der Regel, Felder privat zu machen. Verwende static nicht für gewöhnliche Namen, Abmessungen oder andere Zustandswerte, die sich zwischen Objekten unterscheiden müssen.

Wichtige Erkenntnisse

  • Die Referenz this bezeichnet innerhalb von Instanzcode das aktuelle Objekt.
  • Statische Mitglieder gehören zur Klasse; Instanzmitglieder gehören zu einzelnen Objekten.
  • Eine statische Methode braucht eine Objektreferenz, um Instanzmitglieder zu verwenden.
  • Behebe Fehler zum statischen Kontext, indem du das richtige Objekt bereitstellst, wenn der Zustand zu einem Objekt gehört.
  • Ein public static final-Feld stellt häufig eine gemeinsam genutzte Konstante bereit.
4

Kontrollierter Zugriff und gültiger Zustand

Eine Klasse sollte kontrollieren, welche Werte in ihren Zustand aufgenommen werden können. Private Felder und öffentliche Methoden ermöglichen es der Klasse, jede Änderung zu prüfen und Aufrufern nützliche Operationen anzubieten. Du wählst Zugriffsebenen, schreibst Getter und Setter, wo sie helfen, und schützt Regeln wie einen erlaubten Temperaturbereich. Du siehst außerdem, warum eine private Liste über einen Getter trotzdem nach außen gelangen kann und wie du verhinderst, dass Code außerhalb der Klasse ihre Prüfungen umgeht.

4.1 Privater Zustand und öffentliche Schnittstelle

Kapselung hält den Zustand eines Objekts mit dem Code zusammen, der ihn kontrolliert. Felder sind üblicherweise privat, während Aufrufer öffentliche Operationen verwenden. Das verbirgt Details der Speicherung und gibt der Klasse eine Stelle, an der sie ihre Regeln durchsetzen kann.

Wähle eine Zugriffsebene
ZugriffWer das Mitglied verwenden kann
privateCode in derselben Klasse
Kein ModifikatorCode im selben Paket
protectedDas gleiche Paket sowie Zugriff für Unterklassen nach den Java-Regeln für Unterklassen
publicCode, der auf den deklarierenden Typ zugreifen kann

Ein Paket gruppiert Klassen. Die einfachen Beispiele legen gewöhnliche Klassen ohne package-Deklaration zusammen in einem unbenannten Paket ab. Paketzugriff entsteht, wenn du den Modifikator weglässt; es gibt kein Zugriffsschlüsselwort package, das du einem Feld hinzufügen könntest.

Beginne mit dem restriktivsten sinnvollen Zugriff. Mache Felder privat, die von anderen Klassen benötigten Konstruktoren und Operationen öffentlich und interne Hilfsmethoden privat. Öffentliche Konstanten sind eine häufige Ausnahme bei Feldern.

Gib der Klasse die Kontrolle über Änderungen

Deklariert Pet ein Feld private int age;, kann eine separate Klasse Main nicht luna.age = -5; zuweisen. Der Compiler weist den Zugriff zurück. Ein Aufruf wie luna.setAge(-5) kann erlaubt sein, weil die öffentliche Methode zu Pet gehört und entscheiden kann, ob sie das Argument speichert.

Ein öffentliches Feld würde es jedem Aufrufer erlauben, diese Methode zu umgehen. Sobald alle Änderungen über den Code der Klasse erfolgen, können die Prüfungen an einer Stelle stehen. Privater Zugriff allein validiert ein Alter nicht; die Methoden müssen die Prüfungen ebenfalls durchführen.

Halte die öffentliche Schnittstelle stabil

Angenommen, eine Uhr speichert zunächst separate Felder hours und minutes. Aufrufer verwenden getHours() und getMinutes(). Später ersetzt du die Felder durch einen einzigen Wert minutesSinceMidnight:

public int getHours() {
    return this.minutesSinceMidnight / 60;
}

public int getMinutes() {
    return this.minutesSinceMidnight % 60;
}

Bei 615 gespeicherten Minuten ergibt die Division 10 und der Divisionsrest ist 15. Die Uhr meldet über dieselben Methoden 10:15 Uhr. Die Aufrufer müssen nicht umgeschrieben werden, weil sie von den öffentlichen Operationen abhingen und nicht davon, wie der Zustand gespeichert wurde. Diese Freiheit, Interna zu ändern, ist das Geheimnisprinzip.

Zugriff gehört zu Klassen, nicht zu einzelnen Objekten

Code innerhalb von Pet kann ein privates Feld eines anderen Pet-Objekts lesen. Die Zugriffsberechtigung ergibt sich daraus, dass sich der Code innerhalb der Klasse befindet. Eine andere Klasse auf oberster Ebene erhält keine Berechtigung, indem sie eine Referenz auf ein Haustier hält oder sich im selben Ordner befindet.

Für gewöhnliche Klassendeklarationen auf oberster Ebene stehen public oder kein Modifikator zur Wahl. Ein private class Pet auf oberster Ebene ist unzulässig. Eine öffentliche Klasse auf oberster Ebene gehört in die passende Quelldatei und eine gewöhnliche Quelldatei enthält höchstens eine öffentliche Klasse auf oberster Ebene.

Private Felder bilden eine Grenze im Programmentwurf. Sie verhindern, dass anderer gewöhnlicher Code direkt von internen Feldern abhängt, sind aber kein Mittel, um einen Wert vor einer Person geheim zu halten, die das Programm untersuchen kann. Verwende sie, um die Schnittstelle deiner Klasse zu steuern und ihren Zustand zu schützen.

Wichtige Erkenntnisse

  • Private Felder und öffentliche Operationen halten die Kontrolle über den Zustand innerhalb der Klasse.
  • Lässt du einen Zugriffsmodifikator weg, gilt Paketzugriff.
  • Öffentliche Methoden ermöglichen Änderungen an der internen Speicherung, ohne die Aufrufer zu ändern.
  • Privater Zugriff wird pro Klasse geprüft, sodass Code innerhalb der Klasse auf die Felder einer anderen Instanz zugreifen kann.
  • Gewöhnliche Klassen auf oberster Ebene verwenden öffentlichen Zugriff oder Paketzugriff.

4.2 Zugriffsmethoden, die zur Klasse passen

Eine Klasse kann Aufrufern erlauben, ausgewählte Teile ihres Zustands über Methoden zu lesen oder zu ändern. Entscheide, welche Operationen die Klasse anbieten soll, bevor du für jedes Feld Zugriffsmethoden erstellst.

Gib Werte mit Gettern zurück

Ein Getter, auch Accessor genannt, gibt Informationen zurück, ohne das Objekt zu ändern. Ein gängiger Getter liest ein Feld:

public String getName() {
    return this.name;
}

public int getAge() {
    return this.age;
}

Der Rückgabetyp passt zu den bereitgestellten Informationen. Ein Getter, der etwas ausgibt und void zurückgibt, kann keinen Wert für einen Ausdruck wie pet.getAge() + 1 liefern. Der Aufrufer sollte den Wert erhalten und entscheiden, wie er ihn verwendet.

Ändere ausgewählte Werte mit Settern

Ein Setter, auch Mutator genannt, nimmt einen neuen Wert entgegen und aktualisiert den Zustand:

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

this.name ist das Feld. name ist der Parameter. name = name; wird kompiliert, ändert aber kein Feld. Es gibt hier keinen Rückgabewert, weil dieser Setter eine Änderung durchführen soll.

Für ein Haustier im Alter von 3 Jahren:

luna.setName("Lulu");
System.out.println(luna.getName() + " is " + luna.getAge());
// Lulu is 3

Der Aufrufer greift nie direkt auf ein Feld zu. Der Setter ändert den Namen und die Getter geben die aktuellen Werte zurück, die der Aufrufer verbinden kann.

Halte dich an Namen, die anderer Java-Code erwartet
FeldGetterSetter, falls nötig
pageCountgetPageCount()setPageCount(int pageCount)
namegetName()setName(String name)
lentOutisLentOut()setLentOut(boolean lentOut)

Schreibe den ersten Buchstaben des Feldnamens nach get, set oder dem üblichen boolean-Präfix is groß. Ein Name wie getname() wird zwar möglicherweise kompiliert, folgt aber nicht der Konvention, die Werkzeuge und andere Programmierer erwarten.

Biete nur nützliche Operationen an

Der Titel eines Buches kann einen Getter und keinen Setter haben, weil Aufrufer ihn lesen, aber nicht ersetzen sollen. Ein privates final-Feld kann durchsetzen, dass ihm einmal ein Wert zugewiesen wird. Ein interner Zähler braucht möglicherweise weder Getter noch Setter, wenn nur die Klasse ihn verwendet.

Ein Setter ohne Prüfung kann denselben schädlichen Wert annehmen wie ein öffentliches Feld. Ergänze Prüfungen, wenn eine Änderung Einschränkungen unterliegt. Ziehe auch eine Aktionsmethode in Betracht: celebrateBirthday() drückt aus, dass das Alter um eins steigt, und lend() steht für einen Ausleihversuch. lend() kann den aktuellen Zustand prüfen und eine zweite Ausleihe ablehnen. Auch ein Setter kann diese Prüfung durchführen; der Name der Aktion macht dem Aufrufer die Operation aber klarer.

Ein Getter kann ein Ergebnis berechnen. getArea() kann das Produkt aus Breite und Höhe zurückgeben, ohne dass es ein Feld area gibt.

Wichtige Erkenntnisse

  • Ein Getter gibt Informationen zurück; ein Setter ändert einen ausgewählten Wert.
  • Halte dich an das Benennungsmuster getName, setName und das übliche boolean-Muster isName.
  • Gib Feldern nur die Zugriffsmethoden, die Aufrufer brauchen.
  • Aktionsmethoden wie lend drücken erlaubtes Verhalten klar aus und können Zustandsübergänge prüfen.

4.3 Regeln, die bei jeder Änderung erhalten bleiben

Eine Invariante ist eine Regel für den Zustand eines Objekts, die nach der Konstruktion und nach jeder öffentlichen Operation gilt. Ein Thermostat könnte Zieltemperaturen von 5 °C bis einschließlich 30 °C erlauben. Jede Möglichkeit, seinen Zielwert zu setzen, muss diesen Bereich einhalten.

Entscheide, was eine ungültige Eingabe bedeutet

Drei sinnvolle Reaktionen sind, den alten Wert zu behalten, den Wert auf eine Bereichsgrenze zu begrenzen oder eine Ausnahme zu werfen. Sie haben unterschiedliche Auswirkungen, wähle also für jede Operation eine klare Reaktion.

ReaktionErgebnis eines ungültigen Aufrufs
IgnorierenDer alte Feldwert bleibt unverändert
BegrenzenDie nächstgelegene erlaubte Bereichsgrenze wird gespeichert
Ausnahme werfenDer Aufruf bricht mit einem Fehler wie IllegalArgumentException ab

Für ein nicht negatives Alter eines Haustiers kann ein Setter, der ungültige Werte ignoriert, die Zuweisung nur innerhalb von if (age >= 0) vornehmen. Ist das gespeicherte Alter 4, belässt setAge(-2) es bei 4. Das Alter wird nicht zu 0.

Schreibe Bereichsprüfungen in die Klasse
public static final int MIN_CELSIUS = 5;
public static final int MAX_CELSIUS = 30;
private int target;

public void setTarget(int celsius) {
    if (celsius < MIN_CELSIUS) {
        this.target = MIN_CELSIUS;
    } else if (celsius > MAX_CELSIUS) {
        this.target = MAX_CELSIUS;
    } else {
        this.target = celsius;
    }
}

public void raise() {
    setTarget(this.target + 1);
}

Ausgehend von 20 °C speichert setTarget(35) den Wert 30. setTarget(-3) speichert 5. Nach setTarget(29) speichert ein Aufruf von raise() den Wert 30; ein weiterer Aufruf speichert weiterhin 30. Durch die Wiederverwendung des Setters kann raise() die Obergrenze nicht umgehen.

Prüfungen allein in Main können den Thermostat nicht schützen. Ein anderer Aufrufer könnte sie vergessen. Die Klasse muss die Eingabe dort prüfen, wo sie die Speicherung kontrolliert.

Validiere auch den ersten Zustand
public Thermostat(int target) {
    setTarget(target);
}

Mit diesem Konstruktor, der auf den Wertebereich begrenzt, endet new Thermostat(50) bei 30 °C. Eine direkte Zuweisung this.target = target; würde den ungültigen Anfangszustand 50 zulassen. Eine gemeinsam verwendete private Prüfmethode ist eine weitere Möglichkeit, die Validierung in Konstruktoren und späteren Methoden wiederzuverwenden.

Das Werfen einer Ausnahme ist nützlich, wenn ein ungültiges Argument einen Programmierfehler anzeigt. Ein Konstruktor kann zum Beispiel eine negative Anzahl mit throw new IllegalArgumentException("count must not be negative"); ablehnen. Das Objekt wird nicht erfolgreich konstruiert, sodass der Aufrufer kein fertiges ungültiges Objekt erhält. Eine Eingabe zu ignorieren ist einfacher, geschieht aber ohne Meldung; der Aufrufer braucht möglicherweise eine andere Möglichkeit zu bemerken, dass sich nichts geändert hat.

Halte veränderliche interne Daten im Objekt

Eine private Liste kann dennoch nach außen gelangen, wenn ein Getter die ursprüngliche Liste zurückgibt. Ein Aufrufer könnte dann Einträge hinzufügen oder entfernen, ohne die Methoden der Klasse zu verwenden, und so eine Regel zur Höchstgröße umgehen.

Für ein ArrayList<String>-Feld namens tracks liefert return new ArrayList<>(this.tracks); eine separate Liste mit denselben Strings. Eine Änderung an der zurückgegebenen Liste ändert die interne Liste nicht. Operationen wie addTrack(String title) können Listenänderungen innerhalb der Klasse halten und deren Regeln prüfen, bevor sie die Änderungen annehmen.

Wichtige Erkenntnisse

  • Eine Invariante gilt nach der Konstruktion und nach jeder öffentlichen Operation.
  • Ignorieren, Begrenzen und das Werfen einer Ausnahme sind unterschiedliche Reaktionen auf ungültige Eingaben.
  • Jeder Konstruktor und jede zustandsändernde Methode muss dieselben Regeln einhalten.
  • Verwende die Validierung wieder, damit verschiedene Änderungswege sie nicht umgehen können.
  • Gibt eine Klasse eine interne veränderliche Liste zurück, können Aufrufer ihre Prüfungen umgehen.
  • Eine Kopie einer internen String-Liste schützt die ursprüngliche Liste vor Listenänderungen von außen.
5

Lesbare Objekte und vollständige Klassen

Lesbare Objektbeschreibungen erleichtern es, eine Klasse zu verwenden und zu prüfen. Eine toString-Methode liefert diesen Text überall dort, wo ein Objekt ausgegeben oder mit einem String verknüpft wird. Du schreibst die Methode mit dem richtigen Methodenkopf und fügst dann Felder, Konstruktoren, Methoden und Validierung zu einer vollständigen Klasse zusammen. Ausgehend von einer kurzen Beschreibung setzt du Anforderungen in Code um und testest gewöhnliche Aufrufe, wiederholte Aktionen und Grenzwertfälle.

5.1 Eine Objektbeschreibung als Rückgabewert

Ein nützliches Objekt sollte sich leicht als Text betrachten lassen. Das eingebaute toString() von Java gibt einen Klassennamen, ein @-Zeichen und einen hexadezimalen Hashcode zurück, etwa Pet@8bcc55f. Ein benanntes Paket wird Teil des Klassennamens. Der Hashcode als Text variiert und sagt üblicherweise wenig über den Zustand des Objekts aus.

Gib eine lesbare Beschreibung zurück
@Override
public String toString() {
    return this.name + " (" + this.species + ", "
        + this.age + " years)";
}

Für Luna, eine 3 Jahre alte Katze, ist der zurückgegebene String "Luna (cat, 3 years)". Die Methode liest Felder und baut einen String auf. Sie ändert das Haustier nicht und gibt nichts auf dem Bildschirm aus.

@Override weist den Compiler an zu prüfen, ob die Deklaration tatsächlich eine vorhandene Methode ersetzt. Das erkennt Fehler wie tostring mit einem kleinen s. Die Annotation formatiert das Ergebnis nicht; der Rückgabeausdruck bestimmt den Text.

Erkenne automatische Aufrufe
System.out.println(luna);
String label = "Pet: " + luna;
String details = luna.toString();

Die erste Zeile gibt die lesbare Beschreibung des Haustiers aus. Die zweite erzeugt "Pet: Luna (cat, 3 years)". Die dritte fordert dieselbe Beschreibung ausdrücklich an und speichert sie, ohne sie auszugeben. Wird eine ArrayList von Haustieren ausgegeben, verwendet ihre Darstellung innerhalb der eckigen Klammern das toString() jedes Haustiers.

Wegen der automatischen Umwandlung ist es wichtig, Text zurückzugeben. Der Aufrufer gibt das Ergebnis möglicherweise aus, speichert es oder verknüpft es. Eine Methode, die nur etwas ausgibt, kann den String nicht liefern, den all diese Aufrufer brauchen.

Achte auf den richtigen Methodenkopf
DeklarationWas geschieht
public String toString()Liefert die Objektbeschreibung
public String tostring()Deklariert eine andere Methode; @Override erkennt dies
public String toString(int width)Deklariert eine Überladung; die automatische Ausgabe ruft sie nicht auf
public void toString()Schlägt fehl: Die Methode steht im Konflikt mit der vorhandenen Methode, die einen String zurückgibt

Verwende den genauen parameterlosen Methodenkopf und ergänze die Annotation. Eine Überladung mit Parametern kann eine gültige separate Methode sein, aber die Ausgabe eines Objekts liefert diese Parameter nicht.

Formatiere Werte, ohne sie zu ändern

Eine Uhr braucht zweistellige Stunden und Minuten. Innerhalb ihrer toString()-Methode:

return String.format(java.util.Locale.US,
    "%02d:%02d", this.hours, this.minutes);

Für die Stunde 9 und die Minute 5 ist der String "09:05". Jeder Formatspezifizierer %02d formatiert eine Ganzzahl mit mindestens zwei Stellen und verwendet bei Bedarf eine führende Null. String.format(java.util.Locale.US, "%.2f", 7.5) gibt "7.50" zurück. Das angegebene Gebietsschema macht die Ausgabe mit Dezimalpunkt unabhängig vom standardmäßigen Formatierungsgebietsschema des Computers.

Halte die Beschreibung kurz genug, damit du sie bei der Fehlersuche lesen kannst. Sie kann ungesetzten Zustand aufdecken: Wird ein Namensfeld mit dem Wert null mit einem String verknüpft, entsteht der Text "null", der dich zur Initialisierung zurückführt. Die Beschreibung sollte den aktuellen Zustand wiedergeben. Daher sollte die zweimalige Ausgabe des Objekts weder eine Uhr vorstellen noch einen Geburtstag feiern.

Wichtige Erkenntnisse

  • Schreibe public String toString() ohne Parameter und gib lesbaren Text zurück.
  • Die Annotation @Override lässt den Compiler prüfen, ob die Methode eine vorhandene Methode ersetzt.
  • Die Ausgabe eines Objekts und seine Verknüpfung mit einem String verwenden automatisch toString.
  • Halte toString frei von Zustandsänderungen und überlasse dem Aufrufer die Entscheidung, wohin der Text geht.
  • String.format kann Werte auffüllen und für eine vorhersagbare Ausgabe ein ausdrücklich angegebenes Gebietsschema verwenden.

5.2 Von einer Anforderung zur getesteten Klasse

Beginne damit, was sich ein Objekt merken muss, welche Aktionen Aufrufer von ihm verlangen können und welche Zustände erlaubt sein müssen. Setze dann jeden Teil in Felder, Konstruktoren und Methoden um.

Leite den Entwurf aus einer Beschreibung ab

„Ein Bibliotheksbuch hat einen Titel, einen Autor und eine Seitenzahl. Es kann ausgeliehen und zurückgegeben werden. Solange es bereits ausgeliehen ist, kann es nicht erneut ausgeliehen werden.“

Verwende LibraryBook als Klassennamen. Speichere die drei beschreibenden Werte und einen booleschen Ausleihstatus. Titel, Autor und Seitenzahl bleiben unverändert, mache sie also final. Der Ausleihstatus ändert sich durch aussagekräftige Aktionen. Ein Setter für jedes Feld ist unnötig.

Entwickle die vollständige Klasse
public class LibraryBook {
    private final String title;
    private final String author;
    private final int pages;
    private boolean lentOut;

    public LibraryBook(String title, String author, int pages) {
        this.title = title;
        this.author = author;
        this.pages = pages;
        this.lentOut = false;
    }

    public boolean isLentOut() {
        return this.lentOut;
    }

    public boolean lend() {
        if (this.lentOut) {
            return false;
        }
        this.lentOut = true;
        return true;
    }

    public void giveBack() {
        this.lentOut = false;
    }

    @Override
    public String toString() {
        String status = this.lentOut ? "lent out" : "available";
        return this.title + " by " + this.author + ", "
            + this.pages + " pages, " + status;
    }
}

Der Konstruktor legt fest, dass das Buch verfügbar ist. lend() prüft den vorhandenen Zustand, bevor es ihn ändert; die Rückgabe von false bei einer zweiten Ausleihe erhält die Regel. giveBack() macht das Buch verfügbar. Der Getter meldet den Status und toString() beschreibt das ganze Buch, ohne es zu ändern.

Schreibe erwartete Ergebnisse vor den Aufrufen auf

In der klassischen main-Methode einer separaten Klasse Main:

LibraryBook b = new LibraryBook("Night Train", "A. Rivera", 320);
System.out.println(b);        // Night Train by A. Rivera, 320 pages, available
System.out.println(b.lend()); // true
System.out.println(b.lend()); // false
b.giveBack();
System.out.println(b.lend()); // true

Der zweite Ausleihversuch ist entscheidend: Eine Methode, die immer das Flag setzt und true zurückgibt, würde den ersten Aufruf bestehen, aber an dieser Prüfung scheitern. Teste mit isLentOut() sowohl den Zustand als auch die Rückgabewerte. Probiere außerdem ein zweites Buch aus, um zu prüfen, ob das andere Buch verfügbar bleibt, wenn eines ausgeliehen wird.

Teste dort, wo sich das Verhalten ändert

Decke jeden Konstruktor und jede öffentliche Methode ab. Probiere bei Bereichsprüfungen beide Endpunkte und Werte knapp außerhalb davon. Prüfe bei einer Uhr den Minutenüberlauf und den Übergang zu Mitternacht. Prüfe bei einem Setter, der ungültige Eingaben ablehnt oder ignoriert, ob sie die versprochene Wirkung auf den alten Zustand haben. Schreibe das erwartete Ergebnis zuerst auf, damit eine unerwartete Ausgabe nicht unbemerkt zu deiner neuen Erwartung wird.

Entwickle in kleinen Schritten: zuerst Felder und Konstruktor, dann eine lesbare Beschreibung, dann jeweils eine Operation und ihre Prüfungen. Speichere gewöhnliche öffentliche Klassen in passenden Dateien, kompiliere sie zusammen und starte die Klasse, die main enthält. Trennst du die Operationen des Modells von der Bildschirmausgabe, kann dieselbe Klasse verschiedenen Programmen dienen.

Wichtige Erkenntnisse

  • Mache Daten, die gespeichert bleiben müssen, zu privaten Feldern und erlaubte Aktionen zu Methoden.
  • Schreibe die Zustandsregeln auf, bevor du Konstruktoren und Änderungen implementierst.
  • Verwende final für feste Werte einzelner Objekte und vermeide unnötige Setter.
  • Teste Rückgabewerte und resultierenden Zustand, auch bei wiederholten Aufrufen und getrennten Objekten.
  • Prüfe neben gewöhnlichen Werten auch Bereichsgrenzen und ungültige Eingaben.
  • Entwickle und teste jeweils einen nützlichen Teil.
  • Trenne das Verhalten des Modells vom Hauptprogramm, das Ergebnisse ausgibt.

Java-Klassen und Objekte: Beispiele

5 Interaktive Lektionen

1

Von einer Klasse zu zwei Haustieren

10 Schritte
Erzeuge Objekte, bestimme ihren Zustand und verfolge Aufrufe über getrennte und gemeinsam genutzte Referenzen.
  1. 1
    EinführungEin Typ für die Dinge in deinem Programm

    Ein Haustierprogramm muss sich Name, Tierart und Alter zusammen merken. Eine Klasse gibt dieser Gruppe einen Typ und bietet Methoden an, die sie verwenden. Jedes Objekt ist ein Haustier, das aus dieser Definition erzeugt wird.

    Verfolge den Unterschied zwischen der Definition und den Haustieren, die sie hervorbringt. Verfolge dann zwei Haustierobjekte und sieh, warum eine zusätzliche Variable nicht immer ein zusätzliches Objekt bedeutet.

  2. 2
    AnalogieEin Plan und seine Instanzen

    Stell dir eine Klasse wie einen Bauplan und ihre Objekte wie danach gebaute Häuser vor. Der Plan beschreibt, was jedes Haus enthält, ist aber selbst kein Haus.

    Eine Pet-Definition kann viele Haustiere beschreiben. Jedes hat Felder für dieselben Arten von Daten, aber eines kann eine drei Jahre alte Katze und ein anderes ein fünf Jahre alter Hund sein. Die Definition der Felder erstellt den Plan; die Objekterzeugung liefert konkrete Instanzen.

  3. 3
    BeispielBestimme Zustand und Verhalten

    Die Felder name, species und age eines Haustiers speichern seinen Zustand. Für Luna sind ihre Werte "Luna", "cat" und 3.

    Die Methode celebrateBirthday() stellt Verhalten bereit. Ihr Rumpf kann this.age++; ausführen und damit das Alter des empfangenden Haustiers ändern. Die Methode ist Teil der Klassendefinition; der Wert, den sie ändert, gehört zu einem bestimmten Haustier.

  4. 4
    HerausforderungErzeugt eine Definition ein Haustier?

    Ein Programmierer schreibt eine Klasse Pet mit drei Feldern und einer Geburtstagsmethode. Es wurde kein Ausdruck zur Objekterzeugung ausgeführt. Wie viele Haustierobjekte hat diese Definition erzeugt?

    ---reveal---

    Keines. Die Klasse definiert einen Typ sowie dessen möglichen Zustand und Verhalten. Objekte entstehen, wenn das Programm sie erzeugt, zum Beispiel mit new Pet(...). Drei Felddeklarationen beschreiben drei Daten pro Haustier, nicht drei Haustiere.

  5. 5
    BeispielErzeuge das erste Objekt

    Lies Pet luna = new Pet("Luna", "cat", 3); in Teilen. Pet luna deklariert eine Variable. new Pet(...) erzeugt ein Haustier und führt den Konstruktor mit den drei angegebenen Argumenten aus.

    Der Konstruktor speichert die Anfangswerte. Die von new erzeugte Referenz wird luna zugewiesen und gibt späterem Code damit eine Möglichkeit, auf dieses Objekt zuzugreifen. Die Deklaration Pet luna; allein würde kein Objekt für einen Aufruf bereitstellen.

  6. 6
    ErklärungRufe eine Methode auf ihrem Empfängerobjekt auf

    In luna.celebrateBirthday() bezeichnet der Ausdruck vor dem Punkt das Empfängerobjekt. Die Geburtstagsmethode wird für Luna ausgeführt und erhöht ihr Alter von 3 auf 4.

    int age = luna.getAge(); ruft dann das aktuelle Alter zur weiteren Verwendung ab. Pet.celebrateBirthday() kann kein Haustier auswählen: Dies ist eine Instanzmethode und ein Klassenname liefert kein bestimmtes Objekt.

  7. 7
    BeispielErzeuge ein zweites Haustier

    Führe nun Pet max = new Pet("Max", "dog", 5); aus. Ein zweiter new-Ausdruck erzeugt ein zweites Objekt mit eigenen Feldern.

    Nach einem weiteren luna.celebrateBirthday() ist Luna 5 und Max immer noch 5. Ihr gleiches Alter macht sie nicht zu demselben Objekt. Ein späterer Geburtstag bei Luna ändert nur Lunas Alter, weil das Alter von Max in den Feldern eines anderen Objekts steht.

  8. 8
    KernaussageEine Zuweisung kann ein Objekt gemeinsam nutzbar machen

    Pet other = luna; kopiert die Referenz auf Luna. Es ruft keinen Konstruktor auf und kopiert ihre Felder nicht in ein anderes Haustier.

    Die Namen other und luna verweisen nun auf dasselbe Objekt. Ein Aufruf von other.celebrateBirthday() ändert das Alter dieses Haustiers von 5 auf 6 und auch luna.getAge() zeigt 6. Zähle Objekterzeugungen getrennt von Variablendeklarationen.

  9. 9
    HerausforderungVerfolge eine gemeinsame Referenz und ein separates Haustier

    Zwei separate Haustiere beginnen mit dem Alter 1 für a und 8 für b. Der Code führt Pet c = a; c.celebrateBirthday(); b.celebrateBirthday(); aus. Welches Alter melden a und b?

    ---reveal---

    Sie melden 2 und 9. c nutzt das Objekt von a gemeinsam, sodass sein Aufruf das Alter dieses Haustiers erhöht. Der Aufruf über b erhöht das Alter des separaten Haustiers. Die Klasse stellt beiden dasselbe Geburtstagsverhalten bereit, aber jeder Aufruf hat ein eigenes Empfängerobjekt.

  10. 10
    ZusammenfassungLies Erzeugung und Aufrufe bewusst

    Halte drei Rollen auseinander: Die Klasse definiert den Typ, jedes Objekt speichert seinen eigenen Instanzzustand und eine Referenzvariable verweist auf ein Objekt.

    Markiere beim Lesen von Code jedes new, gleiche seine Argumente mit dem Konstruktor ab und bestimme das Empfängerobjekt, bevor du einen Methodenaufruf verfolgst. Eine einfache Referenzzuweisung ergänzt einen Zugriffsweg auf ein vorhandenes Objekt. Ein zweites new erzeugt ein separates Objekt mit eigenem Zustand.

2

Erzeuge den ersten Zustand korrekt

14 Schritte
Wähle Felder, verfolge Konstruktorzuweisungen und verwende Überladungen, ohne zusätzliche Objekte zu erzeugen.

Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.

  1. 1
    EinführungGib jedem neuen Objekt brauchbare Daten
  2. 2
    ErklärungWähle dauerhaften Zustand
  3. 3
    ErklärungUnterscheide Standardwerte von brauchbaren Werten
  4. 4
    HerausforderungWähle ein Feld oder eine lokale Variable
  5. 5
    ErklärungLies den Konstruktorkopf
  6. 6
    BeispielÜbertrage Parameter in Felder
  7. 7
    TippEine zulässige Zuweisung, die nichts speichert
  8. 8
    HerausforderungFinde das unveränderte Feld
  9. 9
    VerbindungErzeuge auch interne Objekte
  10. 10
    BeispielBiete einen sinnvollen Weg mit Standardwerten an
  11. 11
    ErklärungVerfolge ein Objekt durch die Kette
  12. 12
    HerausforderungUntersuche einen irreführenden new-Ausdruck
  13. 13
    HerausforderungKombiniere ein festes Feld mit einer Überladung
  14. 14
    ZusammenfassungPrüfe jeden Weg zum fertigen Objekt
3

Verfolge Zustandsänderungen und gemeinsam genutzten Zustand

13 Schritte
Arbeite ein Rechteck und eine Uhr durch und unterscheide dann Instanzverhalten von statischen Mitgliedern.

Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.

  1. 1
    EinführungVerfolge, was jeder Aufruf ändert
  2. 2
    ErklärungGib dem Aufrufer einen Wert zurück
  3. 3
    BeispielSkaliere die gespeicherten Abmessungen
  4. 4
    HerausforderungPrüfe das zurückgegebene Ergebnis nach einer Änderung
  5. 5
    BeispielSchreite über Mitternacht hinaus
  6. 6
    TippKorrigiere ein Zurücksetzen, das den Zustand nicht ändert
  7. 7
    KernaussageBerücksichtige jede reguläre Rückkehr
  8. 8
    ErklärungUnterscheide zwei Zählerstände von einem gemeinsamen Zähler
  9. 9
    BeispielStelle in statischem Code ein Objekt bereit
  10. 10
    VerbindungErkenne eine Konstante
  11. 11
    HerausforderungVerfolge die beiden Arten von Zustand
  12. 12
    HerausforderungFinde heraus, warum sich getrennte Objekte gemeinsam ändern
  13. 13
    ZusammenfassungOrdne jede Operation dem zu, wozu sie gehört
4

Schütze jeden Zugriffsweg auf den Zustand

14 Schritte
Verwende Zugriffsmethoden, Validierung und Listenkopien, damit ein Objekt seine Regeln einhält.

Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.

  1. 1
    EinführungMache die Klasse für ihre Regeln verantwortlich
  2. 2
    ErklärungVerberge das Feld hinter einer Operation
  3. 3
    TippWähle den restriktivsten sinnvollen Zugriff
  4. 4
    HerausforderungBestimme, wo privater Zugriff funktioniert
  5. 5
    BeispielGib Informationen mit einem Getter zurück
  6. 6
    VerbindungBiete Aktionen an, die zum Objekt passen
  7. 7
    HerausforderungLesen erlauben, ohne Ersetzen zuzulassen
  8. 8
    ErklärungWähle eine Regel für ungültige Eingaben
  9. 9
    BeispielVerfolge die Begrenzung an der Obergrenze
  10. 10
    BeispielPrüfe den ersten Zustand
  11. 11
    HerausforderungVerfolge das Ignorieren statt des Begrenzens
  12. 12
    KernaussageEine private Liste kann dennoch nach außen gelangen
  13. 13
    HerausforderungSchließe beide ungeprüften Wege
  14. 14
    ZusammenfassungPrüfe die öffentliche Schnittstelle
5

Entwickle und teste eine nützliche Klasse

12 Schritte
Schreibe eine lesbare Beschreibung, implementiere eine Ausleihregel und teste eine vollständige Bibliotheksbuchklasse.

Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.

  1. 1
    EinführungSetze eine Anforderung in sichtbares Verhalten um
  2. 2
    ErklärungVerwende den genauen Methodenkopf für die Beschreibung
  3. 3
    BeispielGib den aktuellen Zustand als Text zurück
  4. 4
    TippFormatiere eine Uhr, ohne sie zu ändern
  5. 5
    HerausforderungKorrigiere die automatische Ausgabe
  6. 6
    BeispielWähle Felder anhand der Anforderung
  7. 7
    BeispielLege den Anfangsstatus fest
  8. 8
    BeispielImplementiere die Regel vor der Änderung
  9. 9
    HerausforderungSage eine Folge vor ihrer Ausführung voraus
  10. 10
    VerbindungTeste die Fälle, die ein einfacher Durchlauf übersieht
  11. 11
    HerausforderungUnterscheide Auskunft von Änderung
  12. 12
    ZusammenfassungEntwickle mit einer überprüfbaren öffentlichen Zusicherung

Java-Klassen und Objekte: Übungsaufgaben

40 Übungsfragen in 4 Sets

Set 1: Erzeuge Objekte und lies ihren Zustand10 Übungsfragen

Frage 1 von 10

Ein Lieferprogramm verwendet eine Klasse Parcel, um mehrere Pakete zu erzeugen. Wie heißt ein Paket, das während der Programmausführung erzeugt wird?
  1. Eine Definition von Parcel.
  2. Eine Instanz von Parcel.
  3. Eine Methode in Parcel.
  4. Ein Parameter von Parcel.
Antwort zeigenAntwort ausblenden
Antwort: B
Eine Instanz ist ein Objekt, das aus einer Klasse erzeugt wird. Daher ist jedes Paket eine Instanz von Parcel. Die Klassendefinition beschreibt die Daten und Operationen für alle diese Objekte; sie ist kein anderer Name für ein bestimmtes Paket. Eine Methode ist eine Operation, die das Objekt ausführen kann.

Frage 2 von 10

Ein Bell-Objekt ist in bell gespeichert und ring() ist eine Instanzmethode. Wie soll das Programm diese Glocke läuten?
  1. Bell.ring(bell);
  2. ring(bell);
  3. new bell.ring();
  4. bell.ring();
Antwort zeigenAntwort ausblenden
Antwort: D
Die Punktnotation ruft ring() auf dem Objekt auf, auf das bell verweist. Die Variable vor dem Punkt liefert das Empfängerobjekt, das eine Instanzmethode braucht. Bell.ring() nennt dagegen die Klasse und kann daher diese Glocke nicht für einen Instanzaufruf auswählen.

Frage 3 von 10

Eine Klasse deklariert private int attempts; ohne Initialisierer oder Zuweisung im Konstruktor. Welchen Wert speichert ein neues Objekt in diesem Feld?
  1. 0
  2. 1
  3. null
  4. Keinen Wert; die Kompilierung schlägt fehl.
Antwort zeigenAntwort ausblenden
Antwort: A
Ein int-Instanzfeld erhält vor einer ausdrücklichen Initialisierung den Standardwert 0. Die Regel, dass einer nicht initialisierten lokalen Variable vor dem Lesen ein Wert zugewiesen werden muss, gilt nicht für Felder. null ist der Standardwert für Felder von Klassentypen, nicht für ein int-Feld.

Frage 4 von 10

Nach new Badge("Visitor") gibt getText() den Wert null zurück. Das Feld lautet private String text;, der Getter gibt es zurück und der Konstruktorrumpf lautet text = text;. Wie sollte der Konstruktor seinen Parameter text speichern?
  1. text = this.text;
  2. String text = "Visitor";
  3. this.text = text;
  4. this.text = this.text;
Antwort zeigenAntwort ausblenden
Antwort: C
Der Parameter verdeckt das Feld, wenn sein bloßer Name verwendet wird. this.text = text; schreibt die Eingabe in das aktuelle Objekt. Die umgekehrte Zuweisung liest dagegen das noch auf null stehende Feld in den Parameter, und eine Zuweisung des Feldes an sich selbst lässt es ebenfalls ungesetzt.

Frage 5 von 10

Eine Klasse hat Ticket() { this(4); } und Ticket(int zones) { this.zones = zones + 1; }. Welchen Wert speichert new Ticket() in zones?
  1. 0
  2. 4
  3. 5
  4. 8
Antwort zeigenAntwort ausblenden
Antwort: C
Der parameterlose Konstruktor delegiert mit dem Argument 4 auf demselben Objekt. Der umfassendere Konstruktor addiert 1 und speichert 5. Die übergebene 4 ist auf dem Weg zur Initialisierung ein Parameterwert; sie ist nicht der endgültige Feldwert, weil der Konstruktor die zusätzliche Berechnung ausführt.

Frage 6 von 10

Eine MinuteClock speichert Stunden von 0–23 und Minuten von 0–59. advance() fügt eine Minute hinzu und behandelt den Stundenüberlauf sowie den Übergang zu Mitternacht. isMidnight() prüft auf 00:00 Uhr. Führe ausgehend von 23:57 Uhr advance(); advance(); advance(); if (isMidnight()) { advance(); } advance(); aus. Wie lautet die endgültige Uhrzeit?
  1. 00:01
  2. 00:02
  3. 00:00
  4. 23:59
Antwort zeigenAntwort ausblenden
Antwort: B
Die ersten drei Aufrufe erreichen 23:58 Uhr, dann 23:59 Uhr und dann 00:00 Uhr. Die Bedingung ist nun erfüllt, sodass ihr Aufruf auf 00:01 Uhr vorstellt; der letzte Aufruf erreicht 00:02 Uhr. Wer bei 00:01 Uhr aufhört, übersieht den unbedingten Aufruf nach dem bedingten Block.

Frage 7 von 10

Jede Lamp muss sich ihren eigenen Ein- oder Ausschaltzustand merken. Wie sollte dieser Zustand deklariert werden?
  1. private boolean on;
  2. private static boolean on;
  3. public static final boolean ON = false;
  4. Ein lokales boolean on innerhalb von switchOn().
Antwort zeigenAntwort ausblenden
Antwort: A
Ein Instanzfeld speichert für jede Lampe einen eigenen Wert und behält ihn zwischen Aufrufen. Wäre das Feld statisch, würden alle Lampen einen Ein- oder Ausschaltwert gemeinsam nutzen. Eine lokale Variable innerhalb der Schaltmethode würde nicht den dauerhaften Zustand pro Lampe bereitstellen, den der Entwurf braucht.

Frage 8 von 10

Ein Feld von Bin wird ohne Zugriffsmodifikator als int capacity; geschrieben. Welchen Zugriff hat es?
  1. Privaten Zugriff.
  2. Geschützten Zugriff.
  3. Öffentlichen Zugriff.
  4. Paketzugriff.
Antwort zeigenAntwort ausblenden
Antwort: D
Ein Mitglied ohne Zugriffsmodifikator hat Paketzugriff, sodass andere Klassen im selben Paket es verwenden können. Es wird nicht allein deshalb privat, weil public weggelassen wurde. Um das Feld hinter den eigenen Methoden der Klasse zu verbergen, deklariere es ausdrücklich mit private.

Frage 9 von 10

Eine Session hat private boolean active;. Aufrufer müssen den Wert prüfen können, dürfen ihn aber nur über start() und stop() ändern. Welcher Accessor passt zu diesem Entwurf?
  1. public void isActive() { System.out.println(this.active); }
  2. public boolean setActive(boolean value) { return value; }
  3. public void setActive(boolean value) { this.active = value; }
  4. public boolean isActive() { return this.active; }
Antwort zeigenAntwort ausblenden
Antwort: D
Der boolesche Getter gibt das aktuelle Flag zurück, ohne Aufrufern eine weitere Möglichkeit zu seiner Änderung zu geben. Die Ausgabe aus einer void-Methode liefert keinen Wert für eine Bedingung des Aufrufers. Ein öffentlicher Setter würde neben den beiden erlaubten Aktionen einen weiteren Änderungsweg eröffnen.

Frage 10 von 10

Ein Objekt definiert public String toString() { return "Ready"; }. Welcher Text entsteht durch System.out.println("State: " + object);?
  1. Ready
  2. State: Ready
  3. State: Object@Ready
  4. State: toString()
Antwort zeigenAntwort ausblenden
Antwort: B
Die String-Konkatenation verwendet die zurückgegebene Textbeschreibung des Objekts, sodass die Beschriftung und "Ready" zu "State: Ready" verbunden werden. Ein eigenes parameterloses toString() ersetzt die eingebaute Beschreibung mit Klasse und Hashcode; Java stellt diesen eingebauten Text nicht zusätzlich voran.
Set 2: Prüfe Initialisierung und kontrollierte Änderungen10 Übungsfragen

Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.

Frage 1 von 10

Ein Locker hat ein Feld occupied und eine Methode release(). Welcher Teil dieser Klasse beschreibt Verhalten?
  1. Das Feld occupied.
  2. Der aktuelle Wert true.
  3. Die Methode release.
  4. Der Variablentyp Locker.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 2 von 10

Ein Panel speichert Breite und Höhe. Seine Breite kann sich ändern und getArea() muss immer die aktuelle Fläche melden. Wie sollte die Fläche dargestellt werden?
  1. Breite mal Höhe in getArea berechnen.
  2. Die Fläche des Konstruktors in einem final-Feld speichern.
  3. Die Fläche einmal in einem gemeinsamen statischen Feld speichern.
  4. Die letzte Fläche in einer lokalen Konstruktorvariable halten.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 3 von 10

Eine Klasse deklariert nur public Sensor(int channel). Ein Aufrufer verwendet new Sensor(). Warum wird das nicht kompiliert?
  1. Der Parameter muss sensor heißen.
  2. Jede Klasse muss einen void-Konstruktor deklarieren.
  3. Java verlangt channel als statisches Feld.
  4. Es ist kein parameterloser Konstruktor verfügbar.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 4 von 10

Ein Messgerät speichert private int reading = 9;. Seine Methode lautet public void reset() { int reading = 0; }. Was meldet ein Getter, der this.reading zurückgibt, nach reset()?
  1. 0
  2. 9
  3. null
  4. Einen Kompilierfehler wegen fehlender Rückgabe.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 5 von 10

Eine Methode rechnet eine übergebene Anzahl von Minuten in Stunden um und verwendet dabei nur ihren Parameter. Sie braucht keine Objektfelder. Welche Art von Methode kann diese Aufgabe erfüllen?
  1. Ein Instanzfeld.
  2. Eine statische Methode.
  3. Eine Konstruktorüberladung.
  4. Eine lokale final-Variable.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 6 von 10

Eine Uhr ersetzt separate Stunden- und Minutenfelder durch ein Feld minutesSinceMidnight, während ihre Getter weiterhin dieselben Werte zurückgeben. Warum können Aufrufer ihre vorhandenen Getter-Aufrufe beibehalten?
  1. Private Felder werden für Aufrufer öffentlich.
  2. Der Compiler ersetzt Getter-Aufrufe durch Felder.
  3. Jeder Aufrufer erhält eine Kopie der alten Felder.
  4. Das öffentliche Verhalten bleibt gleich.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 7 von 10

Ein Messgerät beginnt bei 16. Sein Setter begrenzt Eingaben auf den Bereich von 10 bis einschließlich 20. Welche Werte sind nach setValue(25) und anschließend setValue(8) jeweils gespeichert?
  1. 20, dann 10.
  2. 16, dann 16.
  3. 25, dann 8.
  4. 20, dann 20.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 8 von 10

Eine Group darf höchstens vier Namen enthalten. addName setzt die Begrenzung durch, aber Group(ArrayList<String> names) speichert die übergebene Liste direkt und getNames() gibt die interne Liste zurück. Welcher Entwurf schützt die Begrenzung vor Änderungen durch Aufrufer?
  1. Eingabe validieren und kopieren; eine Kopie zurückgeben.
  2. Eingabe nur kopieren; die interne Liste zurückgeben.
  3. Eingabe nur validieren; die interne Liste zurückgeben.
  4. Nur bei der Rückgabe kopieren; jede Eingabeliste speichern.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 9 von 10

Welchen Rückgabetyp muss die parameterlose Methode toString() verwenden?
  1. void
  2. Object[]
  3. String
  4. boolean
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 10 von 10

Ein Pass ist anfangs unbenutzt. use() gibt false zurück, wenn er bereits benutzt wurde; andernfalls setzt die Methode das Flag und gibt true zurück. reset() setzt das Flag zurück. toString() meldet es nur. Was geben die use-Aufrufe in use(); println(pass); use(); reset(); use(); use(); zurück?
  1. true, true, true, false
  2. true, false, false, false
  3. true, false, true, true
  4. true, false, true, false
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Set 3: Verfolge den Objektzustand durch längere Folgen10 Übungsfragen

Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.

Frage 1 von 10

Ein Marker hat einen Namen und setName. Führe Marker a = new Marker("One"); Marker b = a; a = new Marker("Two"); b.setName("Three"); aus. Was geben a.getName() und b.getName() zurück?
  1. "Three" und "Three".
  2. "Three" und "Two".
  3. "Two" und "One".
  4. "Two" und "Three".
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 2 von 10

Eine Klasse deklariert private int level = 6;. Ihr Konstruktor lautet public Beacon(int level) { this.level = level + 2; }. Was speichert new Beacon(3)?
  1. 6
  2. 5
  3. 3
  4. 8
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 3 von 10

Eine Klasse hat bereits public Tag(String text). Welcher zusätzliche Konstruktor stellt eine eigenständige Überladung dar?
  1. public Tag(String text, int count)
  2. public Tag(String label)
  3. public Tag(String value)
  4. public Tag(String name)
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 4 von 10

Ein Rechteck hat die Breite 2,0 cm und die Höhe 5,0 cm. scale(k) multipliziert beide Abmessungen mit k; area() gibt ihr Produkt zurück. Führe scale(2); double saved = area(); scale(0.5); aus. Was sind saved und die endgültige Rückgabe von area()?
  1. 20,0 und 10,0 Quadratzentimeter.
  2. 40,0 und 20,0 Quadratzentimeter.
  3. 40,0 und 10,0 Quadratzentimeter.
  4. 10,0 und 10,0 Quadratzentimeter.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 5 von 10

Eine neue Klasse Dial hat das Instanzfeld value und das statische Feld created, beide anfangs 0. Jeder Konstruktor erhöht created einmal; turn() erhöht nur den Wert seines Empfängerobjekts. Führe Dial a = new Dial(); Dial b = a; b.turn(); b = new Dial(); b.turn(); b.turn(); aus. Was sind a.value, b.value und created?
  1. 1, 2 und 2.
  2. 2, 2 und 2.
  3. 1, 3 und 2.
  4. 1, 2 und 3.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 6 von 10

Ein Getter soll eine in private String label; gespeicherte Beschriftung melden. Welche Implementierung liefert diesen Wert an den Aufrufer?
  1. public void getLabel() { System.out.println(this.label); }
  2. public void getLabel(String label) { this.label = label; }
  3. public String getLabel() { return "label"; }
  4. public String getLabel() { return this.label; }
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 7 von 10

Ein Wert wird durch setLevel auf den Bereich von 0 bis einschließlich 10 begrenzt. Der Konstruktor ruft diesen Setter auf und raise() ruft setLevel(this.level + 1) auf. Führe ausgehend von new Level(13) die Aufrufe raise(); setLevel(-2); raise(); raise(); aus. Was ist der endgültige Wert?
  1. 10
  2. 2
  3. 1
  4. 0
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 8 von 10

Welchen String gibt String.format(java.util.Locale.US, "%02d:%02d", 4, 7) innerhalb einer Uhrbeschreibung zurück?
  1. "4:7"
  2. "40:70"
  3. "04:07"
  4. "04.07"
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 9 von 10

Ein Seat muss eine zweite Reservierung bis zur Stornierung ablehnen. Ein Test prüft bisher nur den ersten erfolgreichen Aufruf von reserve(). Welcher nächste Aufruf prüft die Ablehnungsregel am besten?
  1. reserve erneut aufrufen, ohne zu stornieren.
  2. Zuerst stornieren, dann reserve erneut aufrufen.
  3. Einen anderen Sitz erzeugen und diesen reservieren.
  4. Die Reservierung stornieren, ohne erneut zu reservieren.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 10 von 10

Ein Thermostat akzeptiert Zielwerte von 6 °C bis einschließlich 28 °C. Welches Eingabepaar testet die Werte knapp außerhalb beider Bereichsgrenzen?
  1. 6 °C und 28 °C.
  2. 5 °C und 29 °C.
  3. 7 °C und 27 °C.
  4. 17 °C und 18 °C.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Set 4: Untersuche Entwürfe und kombiniere die Regeln10 Übungsfragen

Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.

Frage 1 von 10

Ein Programm verwaltet mehrere Pumpen. Jede hat einen eigenen Identifikator und eine eigene aktuelle Geschwindigkeit, und jede kann starten oder stoppen. Wie sollte eine Klasse Pump dies darstellen?
  1. Statische Felder für Identifikator und Geschwindigkeit; Methoden zum Starten und Stoppen.
  2. Instanzfelder für Identifikator und Geschwindigkeit; Methoden zum Starten und Stoppen.
  3. Instanzfelder für Starten und Stoppen; lokale Identifikatoren in jedem Aufruf.
  4. Lokale Geschwindigkeitswerte beim Starten und Stoppen; eine gemeinsame Identifikatorkonstante.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 2 von 10

Ein Token hat einen numerischen Wert und addOne() erhöht ihn. Führe Token a = new Token(2); Token b = a; b.addOne(); Token c = new Token(7); a = c; a.addOne(); b.addOne(); aus. Welche Werte melden a, b und c?
  1. 4, 4 und 8.
  2. 8, 8 und 8.
  3. 8, 4 und 8.
  4. 8, 3 und 7.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 3 von 10

Ein Objekt zur Summenerfassung muss eine Summe zwischen Aufrufen behalten. Seine Methode add(int amount) deklariert derzeit int total = amount; innerhalb der Methode. Welche Änderung der Speicherung ermöglicht das Aufsummieren?
  1. Ein Instanzfeld total deklarieren und in add aktualisieren.
  2. Dieselbe lokale Variable total erneut im Getter deklarieren.
  3. amount nur im Konstruktor als lokale Variable deklarieren.
  4. In jedem add-Aufruf ein lokales final total deklarieren.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 4 von 10

Ein Bucket deklariert private int capacity = 2; und eine leere Instanzliste items. Sein Konstruktor lautet Bucket(int capacity) { capacity = capacity; }. add gibt false zurück, ohne etwas hinzuzufügen, wenn die Listengröße mindestens der gespeicherten Kapazität entspricht; andernfalls fügt die Methode das Element hinzu und gibt true zurück. snapshot() gibt new ArrayList<>(this.items) zurück.

Führe Bucket b = new Bucket(4); b.add("A"); ArrayList<String> view = b.snapshot(); view.add("B"); aus. Rufe dann b.add("C") und b.add("D") auf. Was geben diese beiden letzten Aufrufe zurück und wie viele Elemente enthält b am Ende?

  1. true, true; 3 Elemente.
  2. false, false; 2 Elemente.
  3. true, false; 3 Elemente.
  4. true, false; 2 Elemente.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 5 von 10

Eine Route hat das final-Feld code und das statische Feld created, das anfangs 0 ist. Ihre Konstruktoren lauten Route() { this("local"); created++; } und Route(String code) { this.code = code; created++; }. Nach je einem Aufruf beider Konstruktoren soll created zwei Objekte zählen. Welche Korrektur ergibt die richtige Anzahl?
  1. created++ aus dem String-Konstruktor entfernen.
  2. this("local") durch new Route("local") ersetzen.
  3. Beide Erhöhungen behalten und created zum Instanzfeld machen.
  4. created++ aus dem parameterlosen Konstruktor entfernen.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 6 von 10

Eine Uhr beginnt bei 22:59 Uhr. advance() fügt eine Minute hinzu und behandelt den Übergang nach 24 Stunden. getHour() liest die aktuelle Stunde. Führe int h = getHour(); advance(); if (getHour() != h) { advance(); } advance(); aus. Was sind h und die endgültige Uhrzeit?
  1. 23 und 23:02 Uhr.
  2. 22 und 23:01 Uhr.
  3. 22 und 23:02 Uhr.
  4. 23 und 23:01 Uhr.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 7 von 10

Eine Hilfsmethode soll nur von Code innerhalb ihrer eigenen Klasse auf oberster Ebene aufgerufen werden können. Welcher Zugriffsmodifikator passt?
  1. public
  2. private
  3. protected
  4. Kein Modifikator.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 8 von 10

Ein Rechteck ändert seine Abmessungen durch scale. Sein Getter gibt this.width * this.height zurück und es gibt kein Feld für die Fläche. Warum ist dies ein gültiger Getter-Entwurf?
  1. Er gibt aktuelle Informationen zurück, ohne die Speicherung offenzulegen.
  2. Jeder Getter muss ein Feld mit genau seinem eigenen Namen zurückgeben.
  3. Die Methode erzeugt bei jedem Aufruf ein Feld für die Fläche.
  4. Das Produkt wird nach dem ersten Aufruf zu einer Konstante.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 9 von 10

Ein Album ist auf drei Bildunterschriften begrenzt. Sein Konstruktor lehnt mehr als drei Bildunterschriften ab und speichert new ArrayList<>(captions). getCaptions() gibt eine weitere Kopie zurück; addCaption lehnt bei erreichter Grenze weitere Einträge ab. Ein Aufrufer übergibt zwei Bildunterschriften, fügt der ursprünglichen Liste eine dritte hinzu, ergänzt eine Bildunterschrift im Ergebnis des Getters und ruft dann einmal addCaption auf. Wie viele Bildunterschriften enthält das Album?
  1. 2
  2. 4
  3. 5
  4. 3
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 10 von 10

Eine Door muss open() ablehnen, wenn sie abgeschlossen ist. Eine neue Tür ist anfangs geschlossen und nicht abgeschlossen. lock() gelingt nur bei geschlossener Tür; unlock() schließt sie auf. Welche Testfolge prüft die Ablehnung im abgeschlossenen Zustand und anschließend ein erfolgreiches Öffnen nach dem Aufschließen?
  1. lock; open → false; unlock; open → true.
  2. open → true; lock; open → false; unlock.
  3. lock; unlock; open → true; open → false.
  4. open → true; unlock; lock; open → false.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Hängst du bei deiner eigenen Aufgabe fest? Hausaufgabenhilfe

Schlüsselbegriffe

59 Schlüsselbegriffe, jeweils mit Definition

A

Aktionsmethode
Eine Aktionsmethode benennt eine aussagekräftige Operation, die das Objekt ausführen kann, und steuert eine Änderung oft genauer als ein einfacher Setter.
Die Aktionsmethode lend() prüft den aktuellen Ausleihstatus, lehnt eine erneute Ausleihe ab und meldet, ob die Ausleihe gelungen ist.
Argument
Ein Argument ist ein Wert, der bei einem Methoden- oder Konstruktoraufruf übergeben und anhand seiner Position und eines kompatiblen Typs einem Parameter zugeordnet wird.
In new Clock(9, 5) liefern die Argumente 9 und 5 die Stunde und die Minute für die neue Uhr.

B

Begrenzung auf den Wertebereich
Die Begrenzung auf den Wertebereich ersetzt einen Wert außerhalb eines erlaubten Bereichs durch den nächstgelegenen Endpunkt und belässt Werte innerhalb des Bereichs unverändert.
Bei einer Begrenzung auf 5–30 °C speichert eine Anfrage nach 34 °C den Wert 30 °C, während eine Anfrage nach 18 °C den Wert 18 °C speichert.

D

Defensive Kopie
Eine defensive Kopie ist ein separates Datenobjekt, das zurückgegeben oder gespeichert wird, damit ein anderer Programmteil interne Daten nicht direkt ändern kann.
Eine mit new ArrayList<>(this.tracks) erstellte defensive Kopie hält Listenänderungen von außen von der internen Liste fern.

E

Empfängerobjekt
Das Empfängerobjekt ist das Objekt, auf dem eine Instanzmethode aufgerufen wird; es wird durch den Ausdruck vor dem Punkt in einem Aufruf bezeichnet.
In book.lend() ist das Empfängerobjekt das Buch, auf das book verweist und dessen Ausleih-Flag die Methode prüft.
Erwartetes Ergebnis
Ein erwartetes Ergebnis ist der Rückgabewert, die Ausgabe oder der Zustand, der vor der Ausführung eines Tests anhand einer Anforderung vorausgesagt wird.
Das erwartete Ergebnis eines zweiten lend() bei einem ausgeliehenen Buch ist false bei unverändertem Ausleih-Flag.

F

final-Feld
Einem final-Feld kann nur einmal ein Wert zugewiesen werden. Es eignet sich daher für objektbezogene Daten, die nach der Initialisierung nicht ersetzt werden sollen.
Der Titel eines Buches kann ein final-Feld sein, dem im Konstruktor ein Wert zugewiesen wird und für das Aufrufern kein Setter angeboten wird.

G

Geheimnisprinzip
Das Geheimnisprinzip sorgt dafür, dass Aufrufer von den verfügbaren Operationen eines Objekts abhängen und nicht von Details seiner internen Speicherung.
Das Geheimnisprinzip erlaubt einer Uhr, ihre Felder zu ändern und dabei getHours() für vorhandene Aufrufer gleichbleibend zu halten.
Getter
Ein Getter oder Accessor gibt Informationen über ein Objekt zurück, ohne es zu ändern, und verwendet häufig einen Namen, der mit get beginnt.
Der Getter getArea() kann Breite mal Höhe berechnen, selbst wenn die Klasse kein separates Feld für die Fläche speichert.
Grenzwerttest
Ein Grenzwerttest prüft das Verhalten an oder nahe einer erlaubten Grenze, an der eine Bedingung ändert, ob eine Operation gelingen soll.
Ein Grenzwerttest für einen Zielwertsetter probiert einen erlaubten Endpunkt und einen Wert knapp außerhalb davon, um den Vergleich zu prüfen.
Gültigkeitsbereich
Der Gültigkeitsbereich einer Variable ist der Bereich des Programmtexts, in dem ihr Name verwendet werden kann, um auf diese Variable zu verweisen.
Der Gültigkeitsbereich einer innerhalb eines if-Blocks deklarierten lokalen Variable endet mit diesem Block, sodass späterer Code ihren Namen nicht verwenden kann.

I

IllegalArgumentException
Eine IllegalArgumentException ist eine Ausnahme, die eine Methode oder ein Konstruktor werfen kann, um ein Argument abzulehnen, das gegen die Eingaberegeln verstößt.
Wird bei einer negativen Kapazität eine IllegalArgumentException geworfen, gibt die Konstruktion kein ungültiges Objekt zurück.
Initialisierung
Die Initialisierung gibt einer Variable ihren ersten Wert; die Initialisierung eines Objekts legt die Felder fest, die für einen brauchbaren Anfangszustand nötig sind.
Die Initialisierung einer Liste in einem Wiedergabelistenkonstruktor verwendet new ArrayList<>(), damit anschließend Einträge hinzugefügt werden können.
Inkrementelle Entwicklung
Bei der inkrementellen Entwicklung wird jeweils ein nützlicher Teil entwickelt und geprüft, sodass Fehler leichter gefunden werden, bevor weiterer Code hinzukommt.
Prüfe bei der inkrementellen Entwicklung zuerst den Konstruktor und ergänze und teste dann jeweils eine Operation.
Instanz
Eine Instanz ist ein Objekt, das aus einer bestimmten Klasse erzeugt wurde; der Begriff bezeichnet die Beziehung zwischen diesem Objekt und seinem Typ.
Ein Haustier namens Luna und eines namens Max sind zwei Instanzen von Pet, wenn jedes separat erzeugt wird.
Instanzmethode
Eine Instanzmethode ist eine nicht statische Methode, die für ein bestimmtes Objekt ausgeführt wird und dessen Instanzfelder verwenden kann.
scale ist eine Instanzmethode: a.scale(2) ändert das Rechteck a, während ein separates Rechteck unverändert bleibt.
Instanzvariable
Eine Instanzvariable, auch Instanzfeld genannt, speichert einen Teil des Zustands eines Objekts und existiert so lange wie dieses Objekt.
Die Instanzvariable age jedes Haustiers behält ihren eigenen Wert, obwohl die Deklaration nur einmal in Pet steht.
Invariante
Eine Invariante ist eine Regel für den Zustand, die nach der Konstruktion und nach jeder öffentlichen Operation auf einem Objekt gelten muss.
Die Invariante eines Thermostats hält seinen Zielwert im erlaubten Bereich, selbst wenn ein Aufrufer 40 °C anfordert.

K

Kapselung
Kapselung fasst den Zustand eines Objekts mit den Operationen zusammen, die ihn kontrollieren, und verbirgt interne Daten hinter einer gewählten Schnittstelle.
Die Kapselung in Pet verbindet ein privates Altersfeld mit prüfenden Methoden, die ungültige Änderungen ablehnen.
Klasse
Eine Klasse definiert einen Java-Typ, indem sie die Daten beschreibt, die seine Objekte speichern, und die Methoden, die ihr Verhalten bereitstellen.
Eine Klasse Pet hält Name, Tierart und Alter mit Operationen wie celebrateBirthday() zusammen.
Klassenentwurf
Beim Klassenentwurf werden Zustand, Konstruktionswege, Operationen und Regeln so gewählt, dass eine Klasse eine nützliche Art von Gegenstand darstellen kann.
Der Klassenentwurf setzt eine Bibliotheksbuchbeschreibung in identifizierende Felder, ein Ausleih-Flag und Operationen um, die Ausleihregeln durchsetzen.
Konstante
Eine Konstante ist ein fester Wert, der häufig durch ein static final-Feld bereitgestellt wird, oft öffentlich ist und in UPPER_SNAKE_CASE benannt wird.
Eine Konstante MAX_CELSIUS liefert der Klasse und ihren Aufrufern dieselbe obere Temperaturgrenze.
Konstruktor
Ein Konstruktor ist eine nach der Klasse benannte Deklaration ohne Rückgabetyp, die ein Objekt bei dessen Erzeugung initialisiert.
Der Pet-Konstruktor kopiert den übergebenen Namen, die Tierart und das Alter in die Felder des neuen Haustiers.
Konstruktorverkettung
Bei der Konstruktorverkettung wird mit einem this(...)-Aufruf ein anderer Konstruktor derselben Klasse auf demselben Objekt ausgeführt.
Durch Konstruktorverkettung kann ein neugeborenes Haustier mit this(name, species, 0) die Initialisierung mit einem Standardalter wiederverwenden.

L

Lebensdauer
Die Lebensdauer einer Variable ist der Zeitraum während der Ausführung, in dem ihr Speicher existiert; ein Instanzfeld existiert mit seinem Objekt.
Die Lebensdauer eines Zählerfeldes erstreckt sich über mehrere Erhöhungsaufrufe, während eine temporäre lokale Variable nur während ihres Aufrufs existiert.
Lokale Variable
Eine lokale Variable wird in einem Block innerhalb einer Methode oder eines Konstruktors deklariert und stellt temporären Speicher für diese Ausführung bereit.
Die lokale Variable total innerhalb einer Berechnungsmethode speichert ein vorübergehendes Ergebnis, das sich das Objekt nicht merken muss.

N

new
Das Java-Schlüsselwort new beginnt einen Ausdruck zur Objekterzeugung, der einen Konstruktor ausführt und eine Referenz auf das Objekt liefert.
new Pet("Luna", "cat", 3) erzeugt ein Haustier und liefert drei Argumente für seinen Anfangszustand.
null
Der Java-Wert null bedeutet, dass eine Referenz auf kein Objekt verweist; Felder von Klassentypen erhalten ihn, sofern sie nicht anders initialisiert werden.
Ein Aufruf von add auf einem Listenfeld, das noch null enthält, schlägt fehl, weil kein Listenobjekt erzeugt wurde.

O

Objekt
Ein Objekt ist eine Instanz einer Klasse, die während der Programmausführung existiert und eigene aktuelle Werte in ihren Instanzfeldern hat.
Zwei Haustierobjekte, die mit separaten new-Ausdrücken erzeugt werden, können unterschiedlich alt sein und dabei einer Klassendefinition folgen.

Ö

Öffentliche Schnittstelle
Die öffentliche Schnittstelle einer Klasse umfasst die öffentlichen Operationen und Werte, die Aufrufern zur Verfügung stehen, die auf die Klasse zugreifen können.
Die öffentliche Schnittstelle eines Buches kann lend(), giveBack() und isLentOut() anbieten, während sein Ausleih-Flag privat bleibt.

P

Paketzugriff
Paketzugriff erlaubt Klassen im selben Paket, eine Deklaration zu verwenden, und wird durch Weglassen eines Zugriffsmodifikators gewählt.
Paketzugriff bei int count; erlaubt einer anderen Klasse im selben unbenannten Paket, das Feld zu lesen oder zu ändern.
Parameter
Ein Parameter ist eine benannte Eingabe, die im Kopf einer Methode oder eines Konstruktors deklariert wird und bei jedem Aufruf ein Argument erhält.
Der Parameter age in einem Haustierkonstruktor erhält 3, wenn der Aufruf an dieser Position 3 übergibt.
Parameterloser Konstruktor
Ein parameterloser Konstruktor nimmt keine Eingabewerte entgegen; er kann ausdrücklich deklariert oder von Java als Standardkonstruktor bereitgestellt werden.
Ein ausdrücklicher parameterloser Konstruktor Clock() kann mit this(0, 0) delegieren, um die Uhr bei Mitternacht zu starten.
private
Der Modifikator private beschränkt den Zugriff auf ein Mitglied auf Code innerhalb seiner Klasse, sodass separate Aufrufer auf oberster Ebene die öffentliche Schnittstelle verwenden müssen.
Ein privates Altersfeld lässt pet.age = -1; in einer separaten Klasse Main fehlschlagen, sodass ein kontrollierter Methodenaufruf nötig ist.
protected
Der Modifikator protected erlaubt Zugriff innerhalb desselben Pakets und eingeschränkten Zugriff aus Unterklassencode außerhalb dieses Pakets.
Eine geschützte Hilfsmethode kann von einer anderen Klasse in ihrem Paket aufgerufen werden, auch wenn diese Klasse keine Unterklasse ist.
public
Der Modifikator public erlaubt den Zugriff auf ein Mitglied überall dort, wo der deklarierende Typ zugänglich ist, sodass Aufrufer die angebotene Operation verwenden können.
Eine öffentliche Methode lend() erlaubt Main, eine Ausleihe anzufordern, während das private Statusfeld des Buches verborgen bleibt.
Punktnotation
Die Punktnotation bezeichnet ein Mitglied über ein Empfängerobjekt, etwa eine Objektreferenz, gefolgt von einem Punkt und einem Methodenaufruf.
Die Punktnotation in luna.getAge() teilt Java mit, dass das über luna erreichte Haustier sein Alter melden soll.

R

Referenz
Eine Referenz ist ein Wert, der ein Objekt bezeichnet und einer Variable oder einem Ausdruck ermöglicht, bei Bedarf auf dieses Objekt zuzugreifen.
Nach Pet other = luna; enthalten die beiden Variablen Referenzen auf dasselbe Haustier, sodass beide darauf zugreifen können.
Rekursiver Konstruktoraufruf
Ein rekursiver Konstruktoraufruf ist eine unzulässige Konstruktorkette, die schließlich einen Konstruktor aufruft, der bereits in dieser Kette steht.
Java meldet einen rekursiven Konstruktoraufruf, wenn Box() und Box(int) gegenseitig aneinander delegieren.
Rückgabewert
Ein Rückgabewert ist das Ergebnis, das eine Methode an ihren Aufrufer zurücksendet und das dort gespeichert, ausgegeben oder in einem Ausdruck verwendet werden kann.
Der Rückgabewert von r.area() kann in einem Ausdruck verdoppelt werden; die bloße Ausgabe der Fläche liefert keinen Wert.

S

Schreibgeschützte Eigenschaft
Eine schreibgeschützte Eigenschaft ist eine Information, die Aufrufer lesen können, ohne dass ihnen eine Operation zum direkten Ersetzen angeboten wird.
Ein fester Titel ist eine schreibgeschützte Eigenschaft, wenn Aufrufer getTitle() haben, aber keine Operation, um den Titel zu ersetzen.
Setter
Ein Setter oder Mutator nimmt einen neuen Wert für einen Teil des Objektzustands entgegen und kann diese Eingabe vor dem Speichern prüfen.
Der Setter setAge(int age) kann negative Eingaben ignorieren, sodass das gespeicherte Alter im erlaubten Bereich bleibt.
Standardkonstruktor
Ein Standardkonstruktor ist der parameterlose Konstruktor, den Java bereitstellt, wenn eine Klasse keine Konstruktoren deklariert.
Das Ergänzen eines Konstruktors mit drei Argumenten lässt den Standardkonstruktor entfallen, sodass new Pet() eine ausdrückliche Überladung benötigt.
Standardwert
Ein Standardwert ist der Anfangswert, den Java einem Feld ohne ausdrücklichen Initialisierer zuweist, etwa 0, false oder null.
Ein boolean-Feld hat den Standardwert false; einer lokalen boolean-Variable muss vor dem Lesen ein Wert zugewiesen werden.
Statische Methode
Eine statische Methode gehört zu einer Klasse und hat kein aktuelles Objekt; sie braucht eine Objektreferenz, um Instanzmitglieder zu verwenden.
Eine statische Methode wie main kann Counter c erzeugen und dann über diese Referenz c.increment() aufrufen.
Statischer Kontext
Ein statischer Kontext ist Code ohne aktuelle Instanz, etwa ein statischer Methodenrumpf, in dem der Zugriff auf Instanzmitglieder über ihren bloßen Namen nicht möglich ist.
In einem statischen Kontext hat ein bloßes Altersfeld kein aktuelles Haustier; pet.getAge() stellt dagegen ein bestimmtes Objekt bereit.
Statisches Feld
Ein statisches Feld ist eine klassenweite Variable, die alle Instanzen gemeinsam nutzen, sodass ihr Wert nicht zu einem bestimmten Objekt gehört.
Ein statisches Feld namens created kann jedes Mal erhöht werden, wenn der Konstruktor eine neue Instanz der Klasse initialisiert.
String.format
Die Methode String.format baut Text aus einem Formatstring und Werten auf und unterstützt feste Breiten und Dezimalstellen.
String.format mit java.util.Locale.US und "%.2f" formatiert 7,5 unabhängig vom Standardgebietsschema als "7.50".

T

this
Das Schlüsselwort this verweist innerhalb von Instanzcode auf das aktuelle Objekt und unterscheidet dessen Felder von gleichnamigen Parametern.
Innerhalb des Setters eines Haustiers schreibt this.name = name; die neue Eingabe in das Feld dieses bestimmten Haustiers.
toString
Die Methode toString() gibt die Textdarstellung eines Objekts zurück, sodass Ausgabe und String-Konkatenation lesbare Details zeigen können.
Die toString-Methode einer Uhr gibt "09:05" zurück, sodass die Ausgabe der Uhr eine nützliche Zeit statt Text mit Klasse und Hashcode zeigt.

Ü

Überladen von Konstruktoren
Beim Überladen von Konstruktoren werden für eine Klasse mehrere Konstruktoren mit unterschiedlichen Parameterlisten für verschiedene Eingaben zur Objekterzeugung deklariert.
Durch das Überladen von Konstruktoren kann eine Uhr Stunden und Minuten entgegennehmen oder ohne Argumente bei Mitternacht beginnen.

V

Validierung
Die Validierung prüft eine Eingabe oder vorgeschlagene Änderung anhand der Regeln der Klasse, bevor sie als Teil des Objektzustands angenommen wird.
Eine gemeinsame Validierung in Konstruktor und Setter verhindert, dass einer der beiden Wege einen ungültigen Zustand speichert.
Verhalten
Das Verhalten eines Objekts umfasst die Operationen, die seine Methoden bereitstellen, um seinen Zustand zu lesen, daraus zu berechnen oder ihn zu ändern.
Ein Buch bietet die Verhaltensweisen lend() und giveBack() an, damit Aufrufer seinen Ausleihstatus verwalten können.
Verschattung
Verschattung tritt auf, wenn eine näher liegende Deklaration, etwa ein Parameter, den einfachen Namen eines Feldes in einem Teil des Codes verdeckt.
Die Verschattung durch einen Parameter macht this.age = age; nützlich: Links steht das Feld und rechts die Eingabe.
void
Der Rückgabetyp void deklariert, dass eine Methode keinen Rückgabewert liefert, selbst wenn sie den Zustand ändert oder etwas ausgibt.
Eine Geburtstagsmethode kann void zurückgeben, weil sie das gespeicherte Alter ihres Empfängerobjekts erhöhen soll.

Z

Zugriffsmodifikator
Ein Zugriffsmodifikator ist ein Java-Schlüsselwort wie private, protected oder public, das den Zugriff auf eine Deklaration steuert.
Der Zugriffsmodifikator private hält eine Hilfsmethode innerhalb ihrer Klasse verfügbar, ohne sie anderen Klassen zugänglich zu machen.
Zustand
Der Zustand eines Objekts ist die Gesamtheit der aktuellen Werte, die in seinen Instanzfeldern gespeichert sind, etwa Stunde und Minute einer Uhr.
Nachdem eine Uhr von 08:59 Uhr vorgestellt wurde, enthält ihr Zustand die Stunde 9 und die Minute 0 und ist für den nächsten Aufruf bereit.
Zustandsprotokoll
Ein Zustandsprotokoll hält Feldwerte nach jeder Anweisung fest, um zu zeigen, wie eine Folge von Operationen bestimmte Objekte ändert.
Ein Zustandsprotokoll mit Spalten für zwei Haustiere zeigt, dass ein Geburtstag beim ersten das zweite Haustier unverändert lässt.

#

@Override
Die Annotation @Override weist den Compiler an zu prüfen, ob eine Methodendeklaration eine bereits von einem Obertyp bereitgestellte Methode ersetzt.
@Override über tostring() deckt den Fehler in der Groß- und Kleinschreibung auf, statt die automatische Ausgabe unverändert zu lassen.

Über diese Einheit

Von StudeliFehler melden

Erstelle deine eigenen Lernmaterialien

Mach aus deinen Notizen, Folien und Dokumenten Lernleitfäden, Übungsfragen, Karteikarten und 6 weitere Tools.

Jetzt starten