Java-Klassenmethoden
Füge Instanzmethoden zu einer Java-Klasse hinzu, um Objekten Verhalten zu geben und auf ihre Felder zuzugreifen.
Die Methoden einer Klasse sind das, was ihren Objekten Verhalten verleiht. Felder legen fest, was ein Objekt ist; Methoden beschreiben, was es tun kann. Bisher waren die von dir geschriebenen Methoden static — Hilfsmethoden, die zur Klasse selbst gehören. Dieses Kapitel handelt von Instanzmethoden, die zu jedem Objekt gehören und auf dessen Felder zugreifen.
Der Begriff „Klassenmethoden" ist in Java etwas mehrdeutig. Die meisten Autoren verwenden ihn für innerhalb einer Klasse definierte Methoden — was beide Arten einschließt. Einige verwenden ihn streng für static-Methoden (weil diese buchstäblich zur Klasse gehören). Wir behandeln hier Instanzmethoden; static-Methoden erhalten ein eigenes Kapitel unter Java static.
Eine Instanzmethode
Füge eine Methode ohne static zum Klassenrumpf hinzu, und sie wird Teil jeder Instanz:
public class Circle {
double radius;
double area() {
return Math.PI * radius * radius;
}
}Nun kann jedes Circle-Objekt, das du erstellst, area() beantworten:
Circle c = new Circle();
c.radius = 5;
System.out.println(c.area()); // 78.539...Der Rumpf liest radius ohne Qualifizierung — Java löst den bloßen Namen als this.radius auf, also das Feld des Objekts, auf dem die Methode aufgerufen wurde.
this — der Empfänger
Jede Instanzmethode hat einen unsichtbaren Parameter namens this, der auf das Objekt verweist, auf dem die Methode aufgerufen wurde:
Circle a = new Circle(); a.radius = 2;
Circle b = new Circle(); b.radius = 5;
a.area(); // inside area(), this == a, so this.radius == 2
b.area(); // inside area(), this == b, so this.radius == 5In beiden Fällen wird dieselbe kompilierte Methode ausgeführt; was sich ändert, ist, auf welches Objekt this verweist. Das Kapitel zum this-Schlüsselwort erklärt, wann du this. explizit schreiben musst und wann du es weglassen kannst.
Methoden, die den Zustand ändern
Eine Methode kann auch in Felder schreiben. Solche Methoden werden oft als Mutatoren bezeichnet:
public class Counter {
int count;
void increment() {
count++; // mutates this.count
}
void reset() {
count = 0;
}
int get() {
return count;
}
}
Counter c = new Counter();
c.increment(); c.increment(); c.increment();
System.out.println(c.get()); // 3Mutation durch Methoden ist der Weg, auf dem Objekte sich im Laufe der Zeit verändern, ohne dass externer Code direkt auf ihre Felder zugreift. Methoden, die ein Feld nur lesen und zurückgeben, sind das Gegenbild — sie werden üblicherweise Accessors oder Getter genannt (int get() oben ist ein Beispiel). Lese- und Schreibzugriffe über Methoden zu leiten, anstatt die Felder freizulegen, ist die Kernidee hinter Kapselung: Das Objekt bleibt Herr über seine eigenen Daten.
Andere Methoden von innen aufrufen
Instanzmethoden können andere Methoden desselben Objekts aufrufen, einfach per Name:
public class Rectangle {
double width, height;
double area() { return width * height; }
double perimeter() { return 2 * (width + height); }
String describe() {
return "area=" + area() + ", perimeter=" + perimeter();
}
}Der Compiler wandelt area() und perimeter() innerhalb von describe in this.area() und this.perimeter() um. Sie rufen die Methoden auf dem Rechteck auf, auf dem describe aufgerufen wurde.
Static vs. Instanz — was verwenden?
Die Faustregel: Hängt die Methode von objektspezifischem Zustand ab?
- Wenn ja, ist es eine Instanzmethode (kein
static). - Wenn nein, ist es
static.
public class Math2 {
public static int squared(int n) { // pure calculation — static
return n * n;
}
}
public class Counter {
int count;
public void increment() { // reads/writes this.count — instance
count++;
}
}Ein häufiges Warnsignal ist eine Instanzmethode, die this völlig ignoriert — das ist eine static-Methode im Verkleidung. Das Kapitel Java static geht auf die Abwägungen ein.
Eine Instanzmethode aufzurufen erfordert eine Instanz
Da Instanzmethoden this lesen, kannst du eine nicht ohne ein Objekt aufrufen:
Counter.increment(); // ERROR — increment is not static
new Counter().increment(); // fine — increment is called on a fresh Counter (which is then thrown away)
Counter c = new Counter();
c.increment(); // fine — increment is called on cDas ist der häufigste Compilerfehler, auf den Einsteiger stoßen: eine nicht-statische Methode wird aus einem statischen Kontext referenziert (typischerweise aus main, das selbst statisch ist).
Wenn der Compiler meldet non-static method increment() cannot be referenced from a static context, hast du mit großer Wahrscheinlichkeit eine Instanzmethode ohne Objekt aufgerufen — zum Beispiel Counter.increment() statt c.increment(). Erstelle zuerst eine Instanz und rufe dann die Methode darauf auf.
Überladen und Überschreiben
Instanzmethoden unterstützen beides:
- Überladen: mehrere Methoden mit demselben Namen, aber unterschiedlichen Parameterlisten in derselben Klasse (behandelt in Methodenüberladen).
- Überschreiben: eine Unterklasse ersetzt die geerbte Version einer Methode (behandelt in Methodenüberschreiben).
Das Überladen hast du bereits in Teil 5 kennengelernt. Das Überschreiben ist einer der großen Vorteile der Vererbung und kommt bald zur Sprache.
Methodenrümpfe sind Blöcke
Ein Methodenrumpf ist ein gewöhnlicher Codeblock — darin deklarierte Variablen sind lokal zum Aufruf, der Kontrollfluss funktioniert wie überall sonst, und du kannst frühzeitig zurückkehren. Nichts daran, dass sich der Code in einer Klasse befindet, ändert, wie der Rumpf selbst gelesen wird. Die einzige zusätzliche Fähigkeit besteht darin, dass bloße Bezeichner nun auf die Felder und anderen Methoden des Objekts verweisen können.
Ein ausgearbeitetes Beispiel
Dieses Beispiel vereint Accessors (area, perimeter), einen Mutator (scale) und eine Methode, die andere Methoden aufruft (describe), in einer Klasse. Rectangle wird nur deshalb als static class deklariert, damit die gesamte Demo in eine einzige Datei passt — dieses Schlüsselwort gilt für die verschachtelte Klasse, nicht für ihre Instanzmethoden, die weiterhin auf einem bestimmten Rechteck ausgeführt werden. Beachte, dass das Skalieren von r den Zustand von s unberührt lässt: Jedes Objekt trägt seinen eigenen Zustand.
Was kommt als Nächstes
Bisher hast du Objekte durch new Dog() erstellt und dann Felder Zeile für Zeile befüllt. Das ist umständlich und fehleranfällig — und leicht zu vergessen. Die Lösung ist ein Konstruktor, eine spezielle Methode, die ausgeführt wird, wenn das Objekt erstellt wird. Fahre mit dem Kapitel Konstruktoren fort.