![]() |
|
|||||
6.4.3 Statische Eigenschaften als Objekteigenschaften nutzen
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Hinweis In statischen Methoden gibt es kein this. |
class InStaticNoThis { int a; static void martina() { this.a = 1; // Compilerfehler genauso wie a = 1? } }
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.
|
Beispiel Objekte tauschen Daten über eine gemeinsame statische Variable.
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 } } |
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 Klassenamen. Daher sollten wir in unseren Programmen großgeschriebene Objektnamen meiden.
Beispiel Warum Referenzen mit Kleinbuchstaben 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 suggeriert.
Beispiel Das gleiche Problem haben wir, wenn wir Klassen mit Kleinbuchstaben benennen. Auch das kann irritieren.
class modifier { static void move() { ... } } |
Jetzt könnte jemand modifier.move() schreiben, und der Leser würde annehmen, dass modifier eine Referenz ist, da sie kleingeschrieben ist und move() eine Objektmethode. Wir sehen an diesem Beispiel, dass es wichtig ist, die Namensgebung zu verfolgen.
Statische Variablen werden auch verwendet, um Konstanten zu definieren. Dazu dient zusätzlich das Schlüsselwort final. Damit wird dem Compiler mitgeteilt, dass dieser Variablen nur einmal ein Wert zugewiesen werden darf. Für Variablen bedeutet dies: Es sind Konstanten, jeder spätere Schreibzugriff wäre ein Fehler.
class Sockentyp { static final int PUNKTIERT = 1, GEFLECKT = 2, GESTREIFT = 3; }
In der Klasse Sockentyp werden drei Konstanten definiert. Ein Aufzählungstyp mit enum wie in C++ gibt es in Java nicht. Für Konstanten ist es bedenkenswert, die Konstanten relativ zum Vorgänger zu wählen, um das Einfügen zu vereinfachen. Dann wäre etwa GEFLECKT=PUNKTIERT+1.1
| Tipp Es ist eine gute Idee, die Namen von Konstanten vollständig großzuschreiben, um deren Bedeutung hervorzuheben. |
Konstanten sind eine wertvolle Möglichkeit, den Quellcode aussagekräftiger zu machen. Der herkömmliche Weg geht über Ganzzahl-Konstanten:
public final int KONSTANTE1 = 0; public final int KONSTANTE2 = 1; public final int KONSTANTE3 = 2;
Dieser Weg bringt den Nachteil mit sich, dass die Konstanten nicht unbedingt von jedem angewendet werden müssen und ein Programmierer eventuell direkt die Zahlen oder Zeichenketten einsetzt. Dieses Problem kommt zum Beispiel auf, wenn ein Font-Objekt für die grafische Oberfläche angelegt werden soll, aber unser Gedächtnis versagt, in welcher Reihenfolge die Parameter einzugeben sind. Ein Fallbeispiel:
Font f = new Font( "Dialog", 12, Font.BOLD ):
Leider ist dieses falsch, denn die Parameter für die Zeichensatzgröße und den Schriftstil sind vertauscht. Das Problem ist, dass die Konstanten nur Namen für Werte eines frei zugänglichen Grundtyps sind, und nur der Wert an die Funktion übergeben wird. Niemand kann verbieten, dass direkt die Werte eingetragen werden. Das führt dann zu Fehlern, wie im oberen Fall. In diesem ist 12 die Ganzzahl für den Schriftstil, obwohl es dafür nur die Werte 0, 1, 2 geben sollte. Mit Zeichenketten als Wert der Konstanten kommen wir der Lösung auch nicht näher. Eine gute Möglichkeit von Ganzzahlen wegzukommen ist, Objekte einer Klasse als Konstanten einzusetzen. Folgendes bietet sich an:
Listing 6.11 TypsichereKonstanten.java
public class TypsichereKonstanten { static void func( Muster k ) { if ( k == Muster.GEKRINGELT ) System.out.println( "GEKRINGELT" ); if ( k == Muster.GESTRICHELT ) System.out.println( "GESTRICHELT" ); } public static void main( String args[] ) { func( Muster.GESTRICHELT ); } } final class Muster { static final Muster GEKRINGELT = new Muster(); static final Muster GESTRICHELT = new Muster(); private Muster() // von außen lassen sich keine weiteren { // Objekte erzeugen id = nextId++; } private int id; private static int nextId = 0; // der Deutlichkeit halber }
Die Klasse Muster definiert die Konstanten als statische Attribute vom Typ Muster. Da die Objekte für die Konstanten aber nur einmal vorliegen, lassen sie sich einfach mit ==, wie in func() gezeigt, vergleichen.
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 kann jedoch eine Klasse auch zwischenzeitlich aus dem Speicher entfernt werden.
Beispiel Zwei statische Blöcke mit einer Hauptfunktion.
class StaticBlock { static |
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 ); } } |
Mit diesem Vorgehen ist jedoch der Nachteil verbunden, dass bei Ausnahmen im versteckten Hauptprogramm die JVM unsinnige Fehler meldet. Etwa dass die Klasse StaticNowMain nicht gefunden wurde oder auch eine ExceptionInInitializerError, die statt einer vernünftigen Exception kommt.
1 Leider konnte eine frühe Version des freien IBM-Compiler Jikes nicht erkennen, dass PUNKTIERT+1 zur Übersetzungszeit eine Konstante ist.
| << zurück |
Copyright © Galileo Press GmbH 2003
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.