Objektorientierte Programmierung OOP
KE3 Klassen, Objekte und Felder; Begriffe, Beispiele und Definitionen
KE3 Klassen, Objekte und Felder; Begriffe, Beispiele und Definitionen
-
- 1 / 75
-
Flashcards
Beispiel: return-Anweisung
Wollen wir nun eine Methode liefereRabatt() implementieren so muss diese den aktuellen Wert des Attributs rabatt zurückliefern.
class Rechnung {
// Attribute
double betrag;
double mehrwertsteuer;
double rabatt;
Kunde rechnungsempfaenger;
// Methoden
double liefereRabatt() {
return this.rabatt;
}
}
Eine return-Anweisung in einer Methode ohne Ergebnistyp liefert keinen Wert an den Aufrufer zurück, sie ermöglicht aber, zum Beispiel im Falle von ungültigen Argumenten, die Methode vorzeitig zu beenden. Am Ende jeder Methode ohne Ergebnistyp steht implizit eine return-Anweisung.
In einer Methode mit Ergebnistyp muss am Ende jedes möglichen Ablaufpfades eine return-Anweisung mit einem passenden Ausdruck stehen.
Bemerkung: Getter- und Setter-Methoden
In den meisten Klassen haben wir Methoden, die das Auslesen und Verändern von Attributen ermöglichen, wie zum Beispiel die Methoden legeRabattFest(final double neuerRabatt) und liefereRabatt(). Im Englischen wählt man für solche Methoden in der Regel Namen wie getDiscount() und setDiscount(final double newDiscount). Deswegen werden diese Methoden auch als Getter- und SetterMethoden bezeichnet.
Begriff: Aufruf mit Wertübergabe (engl. call by value)
Wird als Parameter ein Wert eines primitiven Datentyps übergeben, so hat die weitere Verarbeitung dieses Wertes durch die Methode keinerlei Auswirkungen auf die aufrufende Stelle. In diesem Fall spricht man von einem Aufruf mit Wertübergabe (engl. call by value).
Begriff: Aufruf mit Referenz (engl. call by reference)
Wird einer Methode als Parameter ein Objekt übergeben, so handelt es sich, exakt gesprochen, um eine Referenz auf das Objekt. Sofern weitere Referenzen auf dieses Objekt existieren, was in der Regel zumindest an der aufrufenden Stelle der Fall ist, sind Veränderungen des Objekts an allen Referenzen gleichermaßen bemerkbar. Dies wird als Aufruf mit Verweisübergabe (engl. call by reference) bezeichnet.
Wird dem Parameter eine andere Referenz zugewiesen, so hat dies keine Auswirkung auf den Aufrufer.
Aufgabe: Was sind Konstruktoren?
Konstruktoren (engl. constructor) sind spezielle Methoden, um Exemplare einer Klasse zu erzeugen. Konstruktoren sind immer nach derjenigen Klasse benannt, deren Exemplare sie erzeugen. In ihnen werden in der Regel die nötigen Initialisierungen, zum Beispiel die Belegung der Attribute mit Anfangswerten, durchgeführt. Es handelt sich bei einem Konstruktor um eine besondere Methode, die immer das eben erzeugte Objekt zurückliefert. Ein Konstruktor wird nie an einem Objekt oder einer Klasse aufgerufen sondern immer in Zusammenhang mit dem Schlüsselwort new.
Definition: Konstruktordeklaration
Eine Konstruktordeklaration besteht aus einem Kopf und einem Rumpf. Sie genügt folgender Form:
Konstruktorname(Parameterliste) {
Anweisungen
}
Der Name des Konstruktors muss mit dem Namen der Klasse, der er angehört, identisch sein. Anders als bei einer Methodendeklaration hat eine Konstruktordeklaration keinen expliziten Ergebnistyp und der Rumpf muss keine return-Anweisung enthalten. [JLS: § 8.8]
Definition: Signatur eines Konstruktors
Die Signatur eines Konstruktors besteht aus dem Konstruktornamen sowie den Typen seiner formalen Parameter. [JLS: § 8.8.2]
Konstruktorennamen können ebenso wie Methodennamen überladen werden. [JLS: § 8.8.8]
Bemerkung: finale Attribute
Finale Attribute müssen entweder direkt bei der Deklaration initialisiert werden, oder in allen Konstruktoren der Klasse. Anderenfalls entsteht ein Übersetzungsfehler. So können wir unsere Rechnung beispielsweise mit einer Rechnungsnummer versehen, die im Konstruktor gesetzt wird und sich später nicht mehr verändern darf. Wir übergeben die Rechnungsnummer als Parameter. Eine weitere Zuweisung im gleichen Konstruktor oder in einer Methode würde zu einem Übersetzungsfehler führen.
class Rechnung {
// Attribute
double betrag;
double mehrwertsteuer;
double rabatt;
Kunde rechnungsempfaenger;
final int rechnungsnummer;
// Konstruktoren
Rechnung(final int rechnungsnummer) {
this.rechnungsnummer = rechnungsnummer;
}
}
Beispiel: Konstruktor aufruf im Konstrukter
class Rechnung {
// Attribute
double betrag;
double mehrwertsteuer;
double rabatt;
Kunde rechnungsempfaenger;
final int rechnungsnummer;
// Konstruktoren
Rechnung(final int rechnungsnummer) {
this(rechnungsnummer, 0.19);
}
Rechnung(final int rechnungsnummer, final double
mwst) {
this.rechnungsnummer = rechnungsnummer;
this.mehrwertsteuer = mwst;
}
}
Die Exemplarvariablen werden vor der Ausführung des Konstruktors initialisiert.
Definition: Klassenvariable
Eine Klassenvariable wird durch das Schlüsselwort static, das der Attributvereinbarung vorangestellt wird, nach folgendem Muster deklariert [JLS: § 8.3.1.1]:
static Typ Name;
Bemerkung: Klassenvariablen werden auch als Klassenattribute bezeichnet. Um keine Verwir- Klassenattribut rung entstehen zu lassen, können Exemplarvariablen dann auch als Exemplarattribute bezeichnet werden.
Java-Konventionen: Aufruf und deklaration von Klassenattributen
Erfolgt ein Zugriff auf eine Klassenvariable von innerhalb der Klasse, stellt man den Klassennamen zur Verdeutlichung auch voran. In der Klassendeklaration werden in der Regel erst die Klassenvariablen und anschließend die Exemplarvariablen deklariert
Beispiel: Zugriff auf Klassenattribute
class Rechnung {
// Klassenattribute
static int naechsteRechnungsnummer = 10000;
// Exemplarattribute
double betrag;
double mehrwertsteuer;
double rabatt;
Kunde rechnungsempfaenger;
final int rechnungsnummer;
// Konstruktoren
Rechnung() {
this(0.19);
}
Rechnung(double mwst) {
this.mehrwertsteuer = mwst;
this.rechnungsnummer =
Rechnung.naechsteRechnungsnummer;
Rechnung.naechsteRechnungsnummer++;
}
}
Beispiel: Konstanten als Klassenattributen
class Rechnung {
// Klassenattribute
static final double
STANDARD_MEHRWERTSTEUERSATZ = 0.19;
static int naechsteRechnungsnummer = 10000;
// Exemplarattribute
double betrag;
double mehrwertsteuer;
double rabatt;
Kunde rechnungsempfaenger;
final int rechnungsnummer;
// Konstruktoren
Rechnung() {
this(Rechnung.STANDARD_MEHRWERTSTEUER
SATZ);
}
Rechnung(double mwst) {
this.mehrwertsteuer = mwst;
this.rechnungsnummer =
Rechnung.naechsteRechnungsnummer;
Rechnung.naechsteRechnungsnummer++;
}
}
Für die Bezeichner von finalen Klassenattributen gelten die Konventionen finaler lokaler Variablen.
Definition: Klassenmethode
Klassenmethoden werden wie Methoden vereinbart, mit dem Unterschied, dass ihnen das Schlüsselwort static vorangestellt ist. [JLS: § 8.4.3.2]
Definition: Vereinbarung von Feldern
Eine Variable für ein Feld wird wie folgt vereinbart:
ElementTyp[] Feldname;
Der Feldtyp ist durch den Datentyp der Elemente (ElementTyp), die in einem Feld enthalten sind, bestimmt.
Beispiel: Deklaration einer Feldvariable
Die Deklaration
int[] artikelnummern;
bezeichnet ein Feld mit Elementen vom Typ int, das artikelnummern genannt wird.
Bemerkung: Feldtypen sind Objekttypen. Jedes Feld ist somit auch ein Objekt. [JLS: § 4.3.1]
Definition: Erzeugung eines Felds
Einem Feldnamen wird ähnlich wie bei Objekten mittels Schlüsselworts new ein Feldobjekt fester Größe zugewiesen:
Feldname = new ElementTyp[IntAusdruck];
Mit dem Ausdruck auf der rechten Seite der Zuweisung wird ein konkretes Feld mit Elementen des Typs ElementTyp der Größe IntAusdruck angelegt. Die Feldgröße wird durch einen Ganzzahlausdruck bestimmt. Der Ausdruch muss zu einem nichtnegativen Wert evaluiert werden. Die Referenz auf das erzeugte Feld wird der Feldvariablen Feldname, die vom gleichen Typ sein muss, zugewiesen. Die Feldelemente sind von 0 bis IntAusdruck - 1 indiziert. [JLS: § 10.3, § 15.10]
Definition: Vereinbarung und Erzeugung eines Felds
Es ist auch möglich, die Variablenvereinbarung und Felderzeugung zu kombinieren [JLS: § 10.2]:
ElementTyp[] Feldname = new ElementTyp[IntAusdruck];
Statt das Schlüsselwort new zu verwenden, kann ein Feld auch durch eine Aufzählung gebildet und zugleich initialisiert werden [JLS: § 10.6]:
ElementTyp[] Feldname = {v1, v2, ..., vn};
Die Ausdrücke v1, v2, ..., vN müssen Werte des Typs ElementTyp liefern.
Beispiel: Erzeugung eines Felds
Der Ausdruck:
int[] artikelnummern = new int[10];
erzeugt ein Feld mit 10 Elementen vom Typ int und weist es der Feldvariablen artikelnummern zu.
Beispiel: Erzeugung und Initialisierung eines Felds
Das Feld deutscheVokale gibt ein Beispiel für die Aufzählungsdefinition eines Felds:
char[] deutscheVokale = {’a’, ’ä’, ’o’, ’ö’, ’u’, ’ü’, ’e’, ’i’};
Bemerkung: Initialisierung eines Felds
Eine solche Aufzählung kann in dieser Form nur bei der Deklaration verwendet werden. Soll sie auch an anderer Stelle genutzt werden, so müssen der Aufzählung der Feldelemente, das Schlüsselwort new und der Feldtyp vorangestellt werden.
char[] deutscheVokale;
// ...
deutscheVokale = new char[]{’a’, ’ä’, ’o’, ’ö’, ’u’, ’ü’, ’e’, ’i’};
Definition: Länge eines Felds
Jedes Feld besitzt eine finale Exemplarvariable length. Die Größe eines Felds Feldname kann somit im Programm durch den ganzzahligen Ausdruck:
Feldname.length
ermittelt werden. [JLS: § 10.7]
Beispiel: Länge eines eindimensionalen Felds
So liefert beispielsweise der Ausdruck deutscheVokale.length; den Wert 8, wenn die Variable deutscheVokalewie oben dargestellt initialisiert wurde.
Falls ein Feld aus Elementen primitiver Datentypen aufgebaut ist, wird jedes Feldelement mit dem Standardwert dieses Typs belegt. Für ein Ganzzahlfeld ist der Standardwert 0, für Gleitkommazahlen der Wert 0.0, für Zeichenelemente das Zeichen mit dem Ganzzahlcode 0, und für boolean ist es der Wert false. Handelt es sich bei dem Typ der Feldelemente um einen Objekttyp, so sind alle Elemente mit null initialisiert. [JLS: § 15.10.1, § 4,12.5]
Die Werte der Elemente eines Feldes können durch den vordefinierten Indexausdruck: Feldname[IntAusdruck] abgefragt oder verändert werden. Der Indexausdruck muss ein ganzzahliger Wert sein. [JLS: § 10.4]
Feldindizes beginnen immer mit 0. Daher dürfen Indexausdrücke in Feldzugriffen weder negative ganze Zahlen sein, noch dürfen sie den größten Indexwert Feldname.length - 1 überschreiten. Falls der int-Ausdruck, der den Index bestimmt, außerhalb der Feldgrenzen 0 bis Feldname.length - 1 liegt, wird dieser Zugriff einen Laufzeitfehler erzeugen. [JLS: § 10.4]
Beispiel: Feldzugriff
Die obige Vereinbarung der Feldvariable deutscheVokale vorausgesetzt, ist der Ausdruck
deutscheVokale[3]
ein gültiger Zugriff und liefert das Ergebnis ö.
Bemerkung: Belegung eines Felds
Die Elemente eines Felds können durch eine for-Schleife einheitlich mit Anfangswerten belegt werden:
for (int i = 0; i < Feldname.length; i++) {
Feldname[i] = Wert;
}
wobei Wert verträglich mit dem den Typ der Feldelemente sein muss.
Beachten Sie, dass eine als final deklarierte Feldvariable lediglich keine neue Zuweisung an diese Variable zulässt. Eine Veränderung der Feldinhalte ist jedoch trotzdem möglich.
Definition: Erweiterte for-Schleife
Die erweiterte for-Schleife besitzt die folgende Syntax:
for (ElementTyp VarName : Feldname) {
// ...
}
wobei der Typ ElementTyp mit dem Typ der Elemente des Feldes Feldname verträglich sein muss. [JLS: § 14.14.2]
Beispiel: mehrdimensionalen Feldern mit der erweiterten For-Schleife
int[][] meinFeld = new int[4][3];
for (int[] x : meinFeld) {
for (int y : x) {
// y ist das aktuelle Feldelement
}
}
Bemerkung: mehrdimensionale Felder mit erweiterten For-Schleife
Im Gegensatz zu der einfachen for-Schleife gibt es bei dieser Form jedoch keine Variable, die den aktuellen Index im Feld repräsentiert. Somit ist es ohne weitere Hilfsvariable auch nicht möglich zwei Felder parallel zu durchlaufen. Zudem ermöglicht die erweiterte for-Schleife nur einen lesenden Zugriff auf die Feldelemente und somit keine Veränderung der Feldinhalte.