Java Klassenattribute (Felder)
Instanzfelder in einer Java-Klasse deklarieren, Initialwerte setzen und über Objekte darauf zugreifen.
Die Attribute einer Klasse sind die darin deklarierten Variablen — die Daten, die jedes Objekt trägt. Die meisten Autoren nennen sie Felder (so auch die Java Language Specification), und „Attribut" ist der allgemeinere OOP-Begriff; beide meinen dasselbe. In diesem Kapitel geht es darum, wie man sie deklariert, welche Anfangswerte sie haben und wie man sie von außerhalb der Klasse lesen und schreiben kann.
Ein Feld deklarieren
Ein Feld sieht wie eine lokale Variable aus, befindet sich jedoch im Klassenrumpf und nicht innerhalb einer Methode:
public class Book {
String title;
String author;
int pages;
boolean inPrint;
}Jede Instanz von Book erhält eine eigene Kopie dieser vier Felder. Zwei Bücher haben zwei separate title-Strings, zwei separate pages-Ints usw. Sie leben so lange wie das Objekt selbst — bis es keine Referenz mehr darauf gibt.
Standardwerte
Wenn ein Feld nicht initialisiert wird, übernimmt Java die Initialisierung. Die Standardwerte hängen vom Typ ab:
| Typ | Standard |
|---|---|
byte, short, int, long | 0 |
float, double | 0.0 |
char | '�' (das Nullzeichen, Codepunkt 0 — kein Leerzeichen) |
boolean | false |
| beliebiger Objekttyp | null |
Book b = new Book();
System.out.println(b.title); // null
System.out.println(b.pages); // 0
System.out.println(b.inPrint); // falseDies ist ein wesentlicher Unterschied zwischen Feldern und lokalen Variablen innerhalb von Methoden — lokale Variablen erhalten keinen Standardwert, und der Compiler verweigert das Lesen einer Variable, bevor ihr ein Wert zugewiesen wurde. Felder starten immer mit irgendetwas.
Inline-Initialisierer
Ein Feld kann direkt bei der Deklaration einen Wert erhalten:
public class Book {
String title = "Untitled";
int pages = 0;
boolean inPrint = true;
}Der Initialisierer wird einmal pro Objekt ausgeführt, wenn dieses Objekt erstellt wird. Er läuft in Deklarationsreihenfolge, vor dem Konstruktorrumpf. Wenn ein Feld eine komplexere Initialisierung benötigt, ist das Aufgabe des Konstruktors.
Felder lesen und schreiben
Man verwendet <reference>.<field> von außen und einfach <fieldName> (oder this.<fieldName>) von innerhalb der Klasse selbst:
Book b = new Book();
b.title = "Effective Java"; // write
String t = b.title; // readInnerhalb der Klasse:
public class Book {
String title;
void rename(String t) {
title = t; // same as this.title = t;
}
}Das Kapitel zum this-Schlüsselwort erklärt, wann der explizite this.-Qualifizierer benötigt wird und wann nicht.
Namenskonventionen
Java-Feldnamen werden in lowerCamelCase geschrieben:
String firstName; // good
String first_name; // not Java style
String FirstName; // looks like a classBoolean-Felder werden häufig mit den Präfixen is oder has benannt (isPublished, hasIndex), damit sich Aufrufstellen natürlich lesen: if (book.isPublished). Konstanten — Felder, die sich nicht ändern — werden in UPPER_SNAKE_CASE geschrieben und mit static final gekennzeichnet; das wird in den Kapiteln zu static und final behandelt.
Instanzfelder vs. Klassenfelder
Standardmäßig gehören Felder zu jeder Instanz — jedes Objekt hat eine eigene Kopie. Mit static gehört das Feld zur Klasse selbst; es gibt genau eine Kopie, die von allen Instanzen gemeinsam genutzt wird:
public class Counter {
int count; // instance — each Counter has its own
static int total; // class — one shared by all Counters
}
Counter a = new Counter();
Counter b = new Counter();
a.count++; b.count++; b.count++;
Counter.total++;
System.out.println(a.count); // 1
System.out.println(b.count); // 2
System.out.println(Counter.total); // 1, accessed through the classDie vollständige Erklärung findet sich im Kapitel zu static; für den Moment kann man davon ausgehen, dass jedes Feld ein Instanzfeld ist, sofern es nicht ausdrücklich mit static markiert ist.
Öffentliche Felder sind meist ein Fehler
Java erlaubt es, public String title; zu schreiben und externem Code direkten Schreibzugriff zu geben. In der Praxis macht man das fast nie. Sobald ein Feld public ist, verliert man die Möglichkeit, Schreibvorgänge zu validieren, den Typ später zu ändern oder Lesevorgänge mit Logik zu versehen — jeder, der das Feld berührt, ist an seine aktuelle Darstellung gebunden.
Das Standardmuster ist, Felder als private zu markieren und stattdessen Getter und Setter bereitzustellen:
public class Book {
private String title;
public String getTitle() { return title; }
public void setTitle(String t) { this.title = t; }
}Das ist Kapselung. Die Kapitel zu Kapselung und Gettern & Settern beschreiben das vollständige Muster. In frühen Beispielen werden Felder der Übersichtlichkeit halber direkt gezeigt, aber echter Java-Code hält sie privat.
Felder speichern Referenzen, keine Objekte
Wenn der Typ eines Feldes eine Klasse ist (kein primitiver Typ), ist das Feld eine Referenz. Zwei Objekte können dasselbe zugrunde liegende Objekt über ihre Felder gemeinsam nutzen:
public class Author { String name; }
public class Book { Author author; }
Author a = new Author();
a.name = "Joshua Bloch";
Book b1 = new Book(); b1.author = a;
Book b2 = new Book(); b2.author = a; // same Author object
a.name = "J. Bloch";
System.out.println(b1.author.name); // J. Bloch
System.out.println(b2.author.name); // J. BlochEs gelten dieselben Hinweise wie im vorherigen Kapitel: Manchmal ist das beabsichtigt, manchmal ist es ein Fehler. Wenn ein Book seinen eigenen Author haben soll, würde man jedem new Author() zuweisen.
Ein ausgearbeitetes Beispiel
Was kommt als Nächstes
Felder sind der Zustand eines Objekts. Die andere Hälfte — sein Verhalten — steckt in den Methoden, die in der Klasse deklariert sind. Das nächste Kapitel über Klassenmethoden zeigt, wie man Methoden an Instanzen bindet und wie diese auf die soeben definierten Felder zugreifen.