6.4 Statische Methoden und Attribute
 
Exemplarvariablen sind eng mit ihrem Objekt verbunden.1
Wird ein Objekt geschaffen, erhält es einen eigenen Satz von Exemplarvariablen, die zusammen den Zustand des Objekts repräsentieren. Ändert eine Objektmethode den Wert einer Exemplarvariablen in einem Objekt, so hat dies keine Auswirkungen auf die Daten der anderen Objekte; jedes Objekt speichert eine individuelle Belegung. Es gibt jedoch auch Situationen, in denen Eigenschaften oder Methoden nicht direkt einem individuellen Objekt zugeordnet werden. Dazu gehören zum Beispiel die Methoden:
|
sin(), etwa in Math.sin(Math.PI/2.0) |
|
max(), etwa in Math.max(1, -2) |
|
Color.HSBtoRGB(), zum Konvertieren von Farben in Farbräumen |
|
Integer.parseInt(), umwandeln von einem String in den Integer oder in Variablen, die sogar konstant sein können |
|
MAX_INTEGER, die größte darstellbare Zahl |
|
PI aus Math bestimmt die Zahl 3,1415 ... |
 Hier klicken, um das Bild zu Vergrößern
Diese genannten Eigenschaften sind weniger einem konkreten Objekt mit seinem ureigenen Objektzustand zuzuordnen, als vielmehr der Klasse. Diese Art von Zugehörigkeit wird in Java durch statische Eigenschaften unterstützt. Da sie nicht zu einem Objekt gehören wie Objekteigenschaften, nennen wir sie auch Klasseneigenschaften. Die Sinus-Funktion ist ein Beispiel für eine statische Methode der Mathe-Klasse und MAX_INTEGER ein statisches Attribut der Klasse Integer.
6.4.1 Warum statische Eigenschaften sinnvoll sind
 
Statische Eigenschaften haben gegenüber Objekteigenschaften den Vorteil, dass sie im Programm ausdrücken, keinen Zustand vom Objekt zu nutzen. Betrachten wir noch einmal Funktionen aus der Klasse Math. Wenn sie Objektmethoden wären, so würden sie in der Regel mit einem Objektzustand arbeiten. Die Funktionen hätten keine Parameter und sie würden ihre Arbeitswerte nicht aus den Argumenten, sondern aus dem internen Zustand des Objekts nehmen. Das macht aber keine Mathe-Funktion. Um den Sinus eines Winkels zu berechnen, benötigen wir kein spezifisches Mathe-Objekt.
Statische Funktionen sind aus diesem Grunde häufiger als statische Variablen, da sie ihre Arbeitswerte ausschließlich aus den Parametern ziehen. Statische Variablen werden in erster Linie als Konstanten verwendet.
6.4.2 Statische Eigenschaften mit static
 
Um statische Eigenschaften in Java umzusetzen, fügen wir vor der Definition einer Variablen oder einer Methode das Schlüsselwort static hinzu. Für den Zugriff verwenden wir einfach den Klassennamen, den wir wie eine Referenz verwenden. In der UML können statische Eigenschaften durch Unterstreichen markiert werden.
Beispiel Eine statische Funktion und eine statische Variable
Listing 6.10
DiskoUtensilien.java
public class DiskoUtensilien
{
/**
* Platz, die jede Person benötigt.
*/
public final static double PLATZ_PRO_PERSON = 5 /* Quadratmeter */;
/**
* Platz, die eine gegebene Anzahl Personen in einer Disko benötigen.
*
* @param anzahl Anzahl Personen.
* @return Quadratmeter.
*/
public static double platzFuerPersonen( int anzahl )
{
return anzahl * PLATZ_PRO_PERSON;
}
}
Die Eigenschaften können nun leicht mit dem Namen DiskoUtensilien angesprochen werden:
System.out.println( DiskoUtensilien.platzFuerPersonen(290) );
|
6.4.3 Statische Eigenschaften als Objekteigenschaften nutzen
 
Besitzt eine Klasse eine Klasseneigenschaft, so kann sie auch wie ein Objektattribut über die Referenz angesprochen werden. Das bedeutet, dass es zwei Möglichkeiten gibt, wenn ein Objektexemplar existiert und die Klasse ein statisches Attribut hat. Bleiben wir bei unserem obigen Beispiel mit der Klasse DiskoUtensilien. Jetzt können wir für den Zugriff auf PLATZ_PRO_PERSON schreiben:
DiskoMitStatics d = new DiskoUtensilien();
System.out.println( d.PLATZ_PRO_PERSON ); // keine gute Idee
System.out.println( DiskoUtensilien.PLATZ_PRO_PERSON ); // das ist gut so
Die unteren beiden Anweisungen sind identisch. Betrachten wir alleine dieses Codesegment, so ist für uns nicht sichtbar, dass PLATZ_PRO_PERSON eine statische Variable ist. Aus diesem Grund sei der Tipp gegeben, statische Eigenschaften über ihren Klassennamen anzusprechen. Falls eine Klasse nur statische Attribute definiert, so spricht nichts dagegen einen privaten Konstruktor anzugeben – Exemplare werden damit vermieden.
6.4.4 Statische Eigenschaften und Objekteigenschaften
 
Wie wir oben gesehen haben, können wir über eine Objektreferenz auch statische Eigenschaften nutzen. Wir wollen uns aber noch einmal vergewissern, wie Objekteigenschaften und statische Eigenschaften gemischt werden können. Erinnern wir uns daran, dass unsere ersten Programme aus der main()-Methode bestanden, aber unsere anderen Methoden auch static sein mussten. Dies ist sinnvoll, denn eine statische Methode kann – ohne explizite Angabe eines aufrufenden Objekts – nur andere statische Methoden aufrufen. Wie sollte auch eine statische Methode eine Objektmethode aufrufen können, wenn es kein dazugehöriges Objekt gibt? Statische Methoden gibt es immer, wenn es die Klasse gibt. Andersherum kann aber jede Objektmethode eine beliebige statische Methode direkt aufrufen. Genauso verhält es sich mit Attributen. Eine statische Methode kann keine Objektattribute nutzen, da es kein implizites Objekt gibt, auf dessen Eigenschaften zugegriffen werden könnte.
this-Referenzen und statische Eigenschaften
Auch der Einsatz der this-Referenz ist bei statischen Eigenschaften nicht möglich. Das trifft in erster Linie statische Methoden, die eine this-Referenz verwenden wollen.
Hinweis In statischen Funktionen gibt es kein this
|
class InStaticNoThis
{
int a;
static void martina()
{
this.a = 1; // Compilerfehler genauso wie a = 1
}
}
6.4.5 Statische Variablen zum Datenaustausch
 
Der Wert einer statischen Variablen wird bei dem Klassenobjekt gespeichert und nicht bei einem Exemplar der Klasse. Wie wir aber gesehen haben, kann jedes Exemplar einer Klasse auch auf die statischen Variablen der Klasse zugreifen. Da eine statische Variable aber nur einmal pro Klasse vorliegt, führt dies dazu, dass mehrere Objekte sich eine Variable teilen. Somit wird ein Austausch von Informationen über die Objektgrenze hinaus erlaubt. Doch kein Vorteil ohne Nachteil. Es kann bei nebenläufigen Zugriffen zu Problemen kommen. Deshalb müssen wir spezielle Synchronisationsmechanismen nutzen.
 Hier klicken, um das Bild zu Vergrößern
Beispiel Objekte tauschen Daten über eine gemeinsame statische Variable.
Listing 6.11
ShareData.java
class ShareData
{
private static int share ;
public void memorize( int i )
{
share = i;
}
public int retrieve ()
{
return share;
}
public static void main( String args[] )
{
ShareData s1 = new ShareData();
ShareData s2 = new ShareData();
s1.memorize( 2 );
System.out.println( s2.retrieve() ); // ist 2
}
}
|
6.4.6 Warum die Groß- und Kleinschreibung wichtig ist
 
Die Vorgabe der Namenskonvention sagt, Klassennamen sind mit Großbuchstaben zu vergeben und Variablennamen mit Kleinbuchstaben. Treffen wir auf eine Anweisung wie Math.max(a, b), so wissen wir sofort, dass max() eine statische Methode sein muss, weil davor ein Bezeichner steht, der großgeschrieben ist. Dieser kennzeichnet also keine Referenz, sondern einen Klassennamen. Daher sollten wir in unseren Programmen großgeschriebene Objektnamen meiden.
Beispiel Warum Referenzvariablen mit Kleinbuchstaben und Klassennamen mit Großbuchstaben beginnen sollten.
String StringModifier = "What is the Matrix?";
String t = StringModifier.trim();
Die trim()-Methode ist nicht statisch, wie die Anweisung durch die Großschreibung der Variablen suggeriert.
Das gleiche Problem haben wir, wenn wir Klassen mit Kleinbuchstaben benennen. Auch das kann irritieren.
class turnbeutel
{
static void vergessen() { ... }
}
Jetzt könnte jemand turnbeutel.vergessen() schreiben, und der Leser würde annehmen, dass turnbeutel eine Referenz ist, da sie kleingeschrieben ist, und vergessen() eine Objektmethode. Wir sehen an diesem Beispiel, dass es wichtig ist, die Namensgebung zu verfolgen.
|
6.4.7 Statische Blöcke
 
Eine Art Konstruktor für das Klassenobjekt selbst (nicht die Exemplare der Klasse) ist ein static-Block, der in jede Klasse gesetzt werden kann. Der Block wird genau dann ausgeführt, wenn die Klasse vom Klassenlader in die virtuelle Maschine geladen wird. In der Regel geschieht das nur einmal während eines Programmlaufs. Unter gewissen Umständen – ein eigener Klassenlader für die Klasse – kann jedoch eine Klasse auch aus dem Speicher entfernt werden und dann mit einem anderen Klassenlader wieder neu geladen werden.
Beispiel Zwei statische Blöcke mit einer Hauptfunktion
Listing 6.12
StaticBlock.java
class StaticBlock
{
static
{
System.out.println( "Eins" );
}
|
static
{
System.out.println( "Zwei" );
}
public static void main( String args[] )
{
System.out.println( "Jetzt geht’s los." );
}
}
|
Lädt der Klassenlader die Klasse StaticBlock, so führt er zuerst den ersten Block mit der Ausgabe »Eins« aus und dann den Block mit der Ausgabe »Zwei«. Da die Klasse StaticBlock auch das main() besitzt, führt die virtuelle Maschine anschließend die Start-Funktion aus.
Beispiel Mit diesem Trick lassen sich auch Programme ohne main()-Funktion schreiben. In den statischen Block wird einfach das Hauptprogramm geschrieben. Da jedoch die virtuelle Maschine immer noch nach dem main() sucht, müssen wir die Laufzeitumgebung schon vorher beenden. Dies geschieht dadurch, dass mit System.exit() die Bearbeitung abgebrochen wird:
Listing 6.13
StaticNowMain.java
class StaticNowMain
{
static
{
System.out.println( "Jetzt bin ich das Hauptprogramm" );
System.exit( 0 );
}
}
Nicht jede Laufzeitumgebung nimmt das jedoch ohne Murren hin. Weiterhin ist mit diesem Vorgehen der Nachteil verbunden, dass bei Ausnahmen im versteckten Hauptprogramm manche virtuellen Maschinen unsinnige Fehler melden. Etwa, dass die Klasse StaticNowMain nicht gefunden wurde, oder auch eine ExceptionInInitializerError, die statt einer vernünftigen Exception kommt.
|
1 Das Symbol beziehungsweise diese Sichtbarkeit ist seit der UML-Spezifikation 1.4 definiert.
|