Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
1 Java ist auch eine Sprache
2 Sprachbeschreibung
3 Klassen und Objekte
4 Der Umgang mit Zeichenketten
5 Mathematisches
6 Eigene Klassen schreiben
7 Angewandte Objektorientierung
8 Exceptions
9 Generics, innere Klassen
10 Die Klassenbibliothek
11 Threads und nebenläufige Programmierung
12 Datenstrukturen und Algorithmen
13 Raum und Zeit
14 Dateien und Datenströme
15 Die eXtensible Markup Language (XML)
16 Grafische Oberflächen mit Swing
17 Grafikprogrammierung
18 Netzwerkprogrammierung
19 Verteilte Programmierung mit RMI und Web–Services
20 JavaServer Pages und Servlets
21 Applets
22 Midlets und die Java ME
23 Datenbankmanagement mit JDBC
24 Reflection und Annotationen
25 Logging und Monitoring
26 Sicherheitskonzepte
27 Java Native Interface (JNI)
28 Dienstprogramme für die Java-Umgebung
Stichwort

Download:
- ZIP, ca. 14,1 MB
Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Java ist auch eine Insel (8. Auflage) von Christian Ullenboom
Programmieren mit der Java Standard Edition Version 6
Buch: Java ist auch eine Insel (8. Auflage)

Java ist auch eine Insel (8. Aufl.)
8., aktual. Auflage, geb., mit DVD
1.475 S., 49,90 Euro
Galileo Computing
ISBN 978-3-8362-1371-4
Pfeil 6 Eigene Klassen schreiben
Pfeil 6.1 Eigene Klassen mit Eigenschaften deklarieren
Pfeil 6.1.1 Attribute deklarieren
Pfeil 6.1.2 Methoden deklarieren
Pfeil 6.1.3 Die this-Referenz
Pfeil 6.2 Privatsphäre und Sichtbarkeit
Pfeil 6.2.1 Für die Öffentlichkeit: public
Pfeil 6.2.2 Kein Public Viewing – Passwörter sind privat
Pfeil 6.2.3 Wieso nicht freie Methoden und Variablen für alle?
Pfeil 6.2.4 Privat ist nicht ganz privat: Es kommt darauf an, wer’s sieht
Pfeil 6.2.5 Zugriffsmethoden für Attribute deklarieren
Pfeil 6.2.6 Setter und Getter nach der JavaBeans-Spezifikation
Pfeil 6.2.7 Paketsichtbar
Pfeil 6.2.8 Zusammenfassung zur Sichtbarkeit
Pfeil 6.3 Statische Methoden und statische Attribute
Pfeil 6.3.1 Warum statische Eigenschaften sinnvoll sind
Pfeil 6.3.2 Statische Eigenschaften mit static
Pfeil 6.3.3 Statische Eigenschaften über Referenzen nutzen?
Pfeil 6.3.4 Warum die Groß- und Kleinschreibung wichtig ist
Pfeil 6.3.5 Statische Variablen zum Datenaustausch
Pfeil 6.3.6 Statische Eigenschaften und Objekteigenschaften
Pfeil 6.4 Konstanten und Aufzählungen
Pfeil 6.4.1 Konstanten über öffentliche statische finale Variablen
Pfeil 6.4.2 Eincompilierte Belegungen der Klassenvariablen
Pfeil 6.4.3 Typ(un)sichere Aufzählungen
Pfeil 6.4.4 Aufzählungen mit enum
Pfeil 6.5 Objekte anlegen und zerstören
Pfeil 6.5.1 Konstruktoren schreiben
Pfeil 6.5.2 Der Default-Konstruktor
Pfeil 6.5.3 Parametrisierte und überladene Konstruktoren
Pfeil 6.5.4 Copy-Konstruktor
Pfeil 6.5.5 Einen anderen Konstruktor der gleichen Klasse aufrufen
Pfeil 6.5.6 Ihr fehlt uns nicht – der Garbage-Collector
Pfeil 6.5.7 Private Konstruktoren, Utility-Klassen, Singleton, Fabriken
Pfeil 6.6 Klassen- und Objektinitialisierung
Pfeil 6.6.1 Initialisierung von Objektvariablen
Pfeil 6.6.2 Statische Blöcke als Klasseninitialisierer
Pfeil 6.6.3 Initialisierung von Klassenvariablen
Pfeil 6.6.4 Exemplarinitialisierer (Instanzinitialisierer)
Pfeil 6.6.5 Finale Werte im Konstruktor und in statischen Blöcken setzen
Pfeil 6.7 Assoziationen zwischen Objekten
Pfeil 6.7.1 Unidirektionale 1:1-Beziehung
Pfeil 6.7.2 Bidirektionale 1:1-Beziehungen
Pfeil 6.7.3 Unidirektionale 1:n-Beziehung
Pfeil 6.8 Vererbung
Pfeil 6.8.1 Vererbung in Java
Pfeil 6.8.2 Spielobjekte modelliert
Pfeil 6.8.3 Implizite Basisklasse java.lang.Object
Pfeil 6.8.4 Einfach- und Mehrfachvererbung
Pfeil 6.8.5 Sichtbarkeit protected
Pfeil 6.8.6 Konstruktoren in der Vererbung und super
Pfeil 6.9 Typen in Hierarchien
Pfeil 6.9.1 Automatische und explizite Typanpassung
Pfeil 6.9.2 Das Substitutionsprinzip
Pfeil 6.9.3 Typen mit dem binären Operator instanceof testen
Pfeil 6.10 Methoden überschreiben
Pfeil 6.10.1 Methoden in Unterklassen mit neuem Verhalten ausstatten
Pfeil 6.10.2 Mit super an die Eltern
Pfeil 6.10.3 Finale Klassen und finale Methoden
Pfeil 6.10.4 Kovariante Rückgabetypen
Pfeil 6.10.5 Array-Typen und Kovarianz
Pfeil 6.11 Dynamisches Binden/Polymorphie
Pfeil 6.11.1 Unpolymorph bei privaten, statischen und finalen Methoden
Pfeil 6.11.2 Polymorphie bei Konstruktoraufrufen
Pfeil 6.12 Abstrakte Klassen und abstrakte Methoden
Pfeil 6.12.1 Abstrakte Klassen
Pfeil 6.12.2 Abstrakte Methoden
Pfeil 6.13 Schnittstellen
Pfeil 6.13.1 Deklarieren von Schnittstellen
Pfeil 6.13.2 Implementieren von Schnittstellen
Pfeil 6.13.3 Markierungsschnittstellen
Pfeil 6.13.4 Ein Polymorphie-Beispiel mit Schnittstellen
Pfeil 6.13.5 Die Mehrfachvererbung bei Schnittstellen
Pfeil 6.13.6 Keine Kollisionsgefahr bei Mehrfachvererbung
Pfeil 6.13.7 Erweitern von Interfaces – Subinterfaces
Pfeil 6.13.8 Vererbte Konstanten bei Schnittstellen
Pfeil 6.13.9 Abstrakte Klassen und Schnittstellen im Vergleich
Pfeil 6.14 Dokumentationskommentare mit JavaDoc
Pfeil 6.14.1 Einen Dokumentationskommentar setzen
Pfeil 6.14.2 Mit javadoc eine Dokumentation erstellen
Pfeil 6.14.3 HTML-Tags in Dokumentationskommentaren
Pfeil 6.14.4 Generierte Dateien
Pfeil 6.14.5 Dokumentationskommentare im Überblick
Pfeil 6.14.6 JavaDoc und Doclets
Pfeil 6.14.7 Veraltete (deprecated) Typen und Eigenschaften


Galileo Computing - Zum Seitenanfang

6.5 Objekte anlegen und zerstören Zur nächsten ÜberschriftZur vorigen Überschrift

Wenn Objekte mit dem new-Operator angelegt werden, reserviert die Speicherverwaltung des Laufzeitsystems auf dem System-Heap Speicher. Wird das Objekt nicht mehr referenziert, so räumt der Garbage-Collector (GC) in bestimmten Abständen auf und gibt den Speicher an das Laufzeitsystem zurück.


Galileo Computing - Zum Seitenanfang

6.5.1 Konstruktoren schreiben Zur nächsten ÜberschriftZur vorigen Überschrift

Wenn der new-Operator ein Objekt angelegt, wird ein Konstruktor der Klasse automatisch aufgerufen. Mit einem eigenen Konstruktor lässt sich erreichen, dass ein Objekt nach seiner Erzeugung einen sinnvollen Anfangszustand aufweist. Dies kann bei Klassen, die Variablen beinhalten, notwendig sein, weil sie ohne vorherige Zuweisung beziehungsweise Initialisierung keinen Sinn ergäben.

class Dungeon 
{ 
  Dungeon()                        // Konstruktor der Klasse Dungeon 
  { 
  } 
}

Konstruktordeklarationen sehen ähnlich wie Methodendeklarationen aus – so gibt es auch Sichtbarkeiten und Überladung –, doch bestehen zwei deutliche Unterschiede:

  • Konstruktoren tragen immer denselben Namen wie die Klasse.
  • Konstruktordeklarationen besitzen keinen Rückgabetyp, also noch nicht einmal void.

Aufrufreihenfolge

Dass der Konstruktor während der Initialisierung und damit vor einem äußeren Methodenaufruf aufgerufen wird, soll ein kleines Beispiel zeigen.

Listing 6.27 Dungeon.java

public class Dungeon 
{ 
  public Dungeon() 
  { 
    System.out.println( "2. constructor" ); 
  } 
 
  public void play() 
  { 
    System.out.println( "4. play" ); 
  } 
 
  public static void main( String[] args ) 
  { 
    System.out.println( "1. before constructor" ); 
    Dungeon d = new Dungeon(); 
    System.out.println( "3. after constructor" ); 
    d.play(); 
  } 
}

Die Aufrufreihenfolge auf dem Bildschirm ist:

1. before constructor 
2. constructor 
3. after constructor 
4. play

Hinweis UML kennt zwar Attribute und Operationen, aber keine Konstruktoren im Java-Sinn. In einem UML-Diagramm werden Konstruktoren wie Operationen gekennzeichnet, die eben nur so heißen wie die Klasse.


Abbildung 6.4 Klasse Dungeon mit einem Konstruktor und zwei Methoden


Galileo Computing - Zum Seitenanfang

6.5.2 Der Default-Konstruktor Zur nächsten ÜberschriftZur vorigen Überschrift

Wenn wir in unseren Klassen keinen Konstruktor angeben, legt der Compiler automatisch einen Standard-Konstruktor (auch Default-Konstruktor genannt) an. Schreiben wir nur

class Player 
{ 
}

macht der Compiler daraus immer automatisch:

class Player 
{ 
  Player() { } 
}

Der automatisch eingeführte Standard-Konstruktor hat immer die gleiche Sichtbarkeit wie die Klasse. Ist also die Klasse public, wird auch der automatisch eingeführte Konstruktor public sein. Ist die Klasse paketsichtbar, ist es auch der Konstruktor.


Galileo Computing - Zum Seitenanfang

6.5.3 Parametrisierte und überladene Konstruktoren Zur nächsten ÜberschriftZur vorigen Überschrift

Konstruktoren können wie Methoden überladen, also mit unterschiedlichen Parameterlisten deklariert sein. Dies soll auch für den Spieler gelten:

  • Player( String name )
  • Player( String name, String item )

Der Player soll sich mit einem Namen und alternativ auch mit einem Gegenstand initialisieren lassen.

Listing 6.28 com/tutego/insel/game/v6/Player.java, Player

public class Player 
{ 
  public String name; 
  public String item; 
 
  public Player( String name ) 
  { 
    this.name = name; 
  } 
 
  public Player( String name, String item ) 
  { 
    this.name = name; 
    this.item = item; 
  } 
}

Die Nutzung kann so aussehen:

Listing 6.29 com/tutego/insel/game/v6/Playground.java, main()

Player spuderman = new Player( "Spuderman" ) ; 
System.out.println( spuderman.name ); // Spuderman 
System.out.println( spuderman.item ); // null 
 
Player holk      = new Player( "Holk", "green color" ); 
System.out.println( holk.name );      // Holk 
System.out.println( holk.item );      // green color

Wann der Compiler keinen Standard-Konstruktor einfügt

Wenn es mindestens einen ausprogrammierten Konstruktor gibt, legt der Compiler einen Standard-Konstruktor nicht mehr automatisch an. Der Versuch, bei unserer Spieler-Klasse ein Objekt einfach mit dem Standard-Konstruktor über new Player() zu erzeugen, führt zu einem Übersetzungsfehler, da es keinen Compiler-generierten Konstruktor gibt.

Player p = new Player();        // The constructor Player() is undefined

Dass der Standard-Konstruktor nicht angelegt wird, hat seinen guten Grund: Es ließe sich sonst ein Objekt anlegen, ohne dass vielleicht wichtige Variablen initialisiert worden wären. So ist das bei unserem Spieler. Die parametrisierten Konstruktoren erzwingen, dass beim Erzeugen ein Spielername angegeben werden muss, sodass nach dem Aufbau auf jeden Fall ein Spielername vorhanden ist. Wenn wir ermöglichen wollen, dass Entwickler neben den parametrisierten Konstruktoren auch einen parameterlosen Standard-Konstruktoren nutzen können, müssten wir diesen per Hand hinzufügen.

Wie ein nützlicher Konstruktor aussehen kann

Besitzt ein Objekt eine Reihe von Attributen, so wird ein Konstruktor in der Regel diese Attribute initialisieren wollen. Wenn wir eine Unmenge von Attributen in einer Klasse haben, sollten wir dann auch endlos viele Konstruktoren schreiben? Besitzt eine Klasse Attribute, die durch setXXX()-Methoden gesetzt und durch getXXX()-Methoden gelesen werden, so ist es nicht unbedingt nötig, dass diese Attribute im Konstruktor gesetzt werden. Ein Standard-Konstruktor, der das Objekt in einen Initialzustand setzt, ist angebracht; anschließend können die Zustände mit den Zugriffsmethoden verändert werden. Das sagt auch die JavaBean-Konvention. Praktisch sind sicherlich auch Konstruktoren, die die häufigsten Initialisierungsszenarien abdecken. Das Punkt-Objekt der Klasse java.awt.Point lässt sich mit dem Standard-Konstruktor erzeugen, aber auch mit einem parametrisierten, der gleich die Koordinatenwerte entgegennimmt.

Wenn ein Objekt Attribute besitzt, die nicht über setXXX()-Methoden modifiziert werden können, diese Werte aber bei der Objekterzeugung wichtig sind, so bleibt nichts anderes übrig, als die Werte im Konstruktor einzufügen. (Eine setXXX()-Methode, die nur einmalig eine Schreiboperation zulässt, ist nicht wirklich schön.) So arbeiten zum Beispiel Werteobjekte, die einmal im Konstruktor einen Wert bekommen und ihn beibehalten. In der Java-Bibliothek gibt es eine Reihe solcher Klassen, die keinen Standard-Konstruktor besitzen, und nur einige parametrisierte, die Werte erwarten. Die im Konstruktor übergebenen Werte initialisieren das Objekt, und es behält diese Werte das ganze Leben lang. Zu den Klassen gehören Wrapper-Klassen wie Integer und Double oder Color, File und Font.


Galileo Computing - Zum Seitenanfang

6.5.4 Copy-Konstruktor Zur nächsten ÜberschriftZur vorigen Überschrift

Ein Konstruktor ist außerordentlich praktisch, wenn er ein typgleiches Objekt über seinen Parameter entgegennimmt und aus diesem Objekt die Startwerte für seinen eigenen Zustand nimmt. Ein solcher Konstruktor heißt Copy-Konstruktor.

Dazu ein Beispiel: Die Klasse Player bekommt einen Konstruktor, der einen anderen Spieler als Parameter entgegennimmt. Auf diese Weise lässt sich ein schon initialisierter Spieler als Vorlage für die Attributwerte nutzen. Alle Eigenschaften des existierenden Spielers können so auf den neuen Spieler übertragen werden. Die Implementierung kann so aussehen:

Listing 6.30 com/tutego/insel/game/v7/Player.java, Player

public class Player 
{ 
  public String name; 
  public String item; 
 
  public Player() 
  { 
  } 
 
  public Player( Player player ) 
  { 
    name = player.name; 
    item = player.item; 
  } 
}

Die main()-Funktion soll jetzt einen neuen Spieler patric erzeugen und anschließend wiederum einen neuen Spieler tryk mit den Werten von patric initialisieren:

Listing 6.31 com/tutego/insel/game/v7/Playground.java, main()

Player patric = new Player(); 
patric.name = "Patric Circle"; 
patric.item = "knot"; 
 
Player tryk = new Player( patric ); 
System.out.println( tryk.name );  // Patric Circle 
System.out.println( tryk.item );  // knot

Hinweis Wenn die Klasse Player neben dem parametrisierten Konstruktor Player(Player) einen zweiten, Player(Object), deklarieren würde, käme es bei einem Nutzen durch new Player(patric) auf den ersten Blick zu einem Konflikt, denn beide Konstruktoren würden passen. Der Java-Compiler löst das so, dass er immer den spezifischsten Konstruktor aufruft, also Player(Player) und nicht Player(Object). Das gilt auch für new Player(null) – auch hier wird der Konstruktor Player(Player) bemüht. Während die Frage für den Alltag nicht so bedeutend ist, müssen sich Kandidaten der Java-Zertifizierung »Sun Certified Java Programmer« auf eine solche Frage einstellen.



Galileo Computing - Zum Seitenanfang

6.5.5 Einen anderen Konstruktor der gleichen Klasse aufrufen Zur nächsten ÜberschriftZur vorigen Überschrift

Mitunter werden zwar verschiedene Konstruktoren angeboten, aber nur in einem Konstruktor verbirgt sich die tatsächliche Initialisierung des Objekts. Nehmen wir unser Beispiel mit dem Konstruktor, der einen Spieler als Vorlage über einen Parameter nimmt, aber auch einen anderen Konstruktor, der den Namen und den Gegenstand direkt entgegennimmt:

Listing 6.32 com/tutego/insel/game/v8/Player.java, Player

public class Player 
{ 
  public String name; 
  public String item; 
 
  public Player( Player player ) 
  { 
    name = player.name; 
    item = player.item; 
  } 
 
  public Player( String name, String item ) 
  { 
    this.name = name; 
    this.item = item; 
  } 
}

Zu erkennen ist, dass beide Konstruktoren die Objektvariablen initialisieren und letztlich das Gleiche machen. Schlauer ist es, wenn der Konstruktor Player(Player) den Konstruktor Player(String, String) der eigenen Klasse aufruft. Dann muss nicht gleicher Programmcode für die Initialisierung mehrfach ausprogrammiert werden. Java lässt eine solche Konstruktorverkettung mit dem Schlüsselwort this zu.

Listing 6.33 com/tutego/insel/game/v9/Player.java, Player

public class Player 
{ 
  public String name; 
  public String item; 
 
  public Player() 
  { 
    this( "", "" ); 
  } 
 
  public Player( Player player ) 
  { 
    this( player.name, player.item ); 
  } 
 
  public Player( String name, String item ) 
  { 
    this.name = name; 
    this.item = item; 
  } 
}

Der Gewinn gegenüber der vorherigen Lösung ist, dass es nur eine zentrale Stelle gibt, die im Fall von Änderungen angefasst werden müsste. Nehmen wir an, wir hätten zehn Konstruktoren für alle erdenklichen Fälle in genau diesem Stil implementiert. Tritt der unerwünschte Fall ein, dass wir auf einmal in jedem Konstruktor etwas initialisieren müssen, so muss der Programmcode – etwa ein Aufruf der Methode init() – in jeden der Konstruktoren eingefügt werden. Dieses Problem umgehen wir einfach, indem wir die Arbeit auf einen speziellen Konstruktor verschieben. Ändert sich nun das Programm in der Weise, dass beim Initialisieren überall zusätzlicher Programmcode ausgeführt werden muss, ändern wir eine Zeile in dem konkreten, von allen benutzten Konstruktor. Damit fällt für uns wenig Änderungsarbeit an – unter softwaretechnischen Gesichtspunkten ein großer Vorteil. Überall in den Java-Bibliotheken lässt sich diese Technik wiedererkennen.

Einschränkungen von this()

Beim Aufruf eines anderen Konstruktors mittels this() gibt es zwei wichtige Beschränkungen:

  • Der Aufruf von this() muss die erste Anweisung des Konstruktors sein.
  • Als Parameter von this() können keine Objektvariablen übergeben werden. Insbesondere Eigenschaften aus der Oberklasse sind noch nicht präsent. Möglich sind aber statische finale Variablen (Konstanten).

Die erste Einschränkung besagt, dass das Erzeugen eines Objektes immer das Erste ist, was ein Konstruktor leisten muss. Nichts darf vor der Initialisierung ausgeführt werden. Die zweite Einschränkung hat damit zu tun, dass die Objektvariablen erst nach dem Aufruf von this() initialisiert werden, sodass ein Zugriff unsinnig wäre – die Werte wären im Allgemeinen 0.

Listing 6.34 Stereo.java

public class Stereo 
{ 
  static         final int STANDARD = 1000; 
  /*non-static*/ final int standard = 1000; 
  public int watt; 
 
  public Stereo() 
  { 
    // this( standard ); 
    // If you comment out the previous line, you will get a compiler error: 
    // Cannot refer to an instance field standard while explicitly invoking a  
    // constructor 
    this( STANDARD ); 
  } 
 
 
  public Stereo( int watt ) 
  { 
    this.watt = watt; 
  } 
}

Da Objektvariablen bis zu einem bestimmten Punkt noch nicht initialisiert sind (was das nächste Unterkapitel erklärt), lässt uns der Compiler nicht darauf zugreifen – nur statische Variablen sind als Übergabeparameter erlaubt. Daher ist der Aufruf this(standard) nicht gültig, da standard eine Objektvariable ist; this(STANDARD) ist jedoch in Ordnung, weil STANDARD eine statische Variable ist.


Galileo Computing - Zum Seitenanfang

6.5.6 Ihr fehlt uns nicht – der Garbage-Collector Zur nächsten ÜberschriftZur vorigen Überschrift

Glücklicherweise werden wir beim Programmieren von der lästigen Aufgabe befreit, Speicher von Objekten freizugeben. Wird ein Objekt nicht mehr referenziert, findet der Garbage-Collector [Lange Tradition hat der Garbage-Collector unter LISP und unter Smalltalk, aber auch Visual Basic benutzt einen GC.] dieses Objekt und kümmert sich um alles Weitere – der Entwicklungsprozess wird dadurch natürlich vereinfacht. Der Einsatz eines GC verhindert zwei große Probleme:

  • Ein Objekt kann gelöscht werden, aber die Referenz existiert noch (engl. dangling pointer).
  • Kein Zeiger verweist auf ein bestimmtes Objekt, dieses existiert aber noch im Speicher (engl. memory leaks).

Prinzipielle Arbeitsweise des Müllaufsammlers

Der GC erscheint hier als ominöses Ding, das die Objekte clever verwaltet. Doch was ist der GC? Implementiert ist er als unabhängiger Thread mit niedriger Priorität. Er verwaltet die Wurzelobjekte, von denen aus das gesamte Geflecht der lebendigen Objekte (der so genannte Objektgraph) erreicht werden kann. Dazu gehören die Wurzel des Thread-Gruppen-Baums und die lokalen Variablen aller aktiven Methodenaufrufe (Laufzeitkeller aller Threads). In regelmäßigen Abständen markiert der Nihilator nicht benötigte Objekte und entfernt sie.

Dank der HotSpot-Technologie, die seit Java 1.3 fester Bestandteil des Java SDK ist, geschieht das Anlegen von Objekten unter der Java VM von Sun sehr schnell. HotSpot verwendet einen generationenorientierten GC, der ausnutzt, dass zwei Gruppen von Objekten mit deutlich unterschiedlicher Lebensdauer existieren. Die meisten Objekte sterben sehr jung, die wenigen überlebenden Objekte werden hingegen sehr alt. Die Strategie dabei ist, dass Objekte im »Kindergarten« erzeugt werden, der sehr oft nach toten Objekten durchsucht wird und in der Größe beschränkt ist. Überlebende Objekte kommen nach einiger Zeit aus dem Kindergarten in eine andere Generation, die nur selten vom GC durchsucht wird. Damit folgt der GC der Philosophie von Auffenberg, der meinte: »Verbesserungen müssen zeitig glücken; im Sturm kann man nicht mehr die Segel flicken.« Das heißt: Der GC arbeitet ununterbrochen und räumt auf. Er beginnt nicht erst mit der Arbeit, wenn es zu spät und der Speicher schon voll ist.

Die manuelle Nullung und Speicherlecks

Bei folgendem Szenario wird der GC das nicht mehr benötigte Objekt hinter der Referenz-variablen ref entfernen können, wenn die Laufzeitumgebung den inneren Block verlässt.

{ 
  { 
    StringBuffer ref = new StringBuffer(); 
  } 
  // StringBuffer ref ist frei für den GC 
}

In fremden Programmen sind mitunter Anweisungen wie die folgende zu lesen:

ref = null;

Oftmals sind sie unnötig, denn wie im Fall unseres Blockes weiß der GC, wann der letzte Verweis vom Objekt genommen wurde. Anders sieht das aus, wenn die Lebensdauer der Variablen größer ist, etwa bei einer Objekt- oder sogar statischen Variablen oder wenn sie in einem Feld referenziert wird. Wenn dann das referenzierte Objekt nicht mehr benötigt wird, sollte die Variable (oder der Feldeintrag) mit null belegt werden, da andernfalls der GC das Objekt aufgrund der starken Referenzierung nicht wegräumen würde. Zwar findet der GC jedes nicht mehr referenzierte Objekt, aber die Fähigkeit zur Devination, Speicherlecks durch unbenutzte, aber referenzierte Objekte aufzuspüren, hat er nicht.


Galileo Computing - Zum Seitenanfang

6.5.7 Private Konstruktoren, Utility-Klassen, Singleton, Fabriken topZur vorigen Überschrift

Ein Konstruktor kann privat sein, was verhindert, dass von außen ein Exemplar dieser Klasse gebildet werden kann. Was auf den ersten Blick ziemlich beschränkt erscheint, erweist sich als ziemlich clever, wenn damit die Exemplarbildung bewusst verhindert werden soll. Sinnvoll ist das etwa bei den so genannten Utility-Klassen. Das sind Klassen, die nur statische Funktionen besitzen, also Hilfsklassen sind. Beispiele für diese Hilfsklassen gibt es zur Genüge, zum Beispiel Math. Warum sollte es hier Exemplare geben? Für den Aufruf von max() ist das nicht nötig. Also wird die Bildung von Objekten erfolgreich mit einem privaten Konstruktor unterbunden.

Wenn ein Konstruktor privat ist, bedeutet das noch lange nicht, dass keine Exemplare mehr erzeugt werden können. Ein privater Konstruktor besagt nur, dass er von außen nicht sichtbar ist – aber die Klasse selbst kann ihn ebenso wie private Methoden »sehen« und zur Objekterzeugung nutzen. Objektmethoden kommen dafür nicht in Frage, da ähnlich wie beim Henne-Ei-Problem ja vorher ein Objekt nötig wäre. Es bleiben somit die statischen Funktionen.

Singleton

Ein Singleton stellt sicher, dass es von einer Klasse nur ein Exemplar gibt. Nützlich ist das für »Dinge«, die es nur genau einmal in einer Applikation geben soll, etwa einen Logger für Protokollierungen.

Listing 6.35 Logger.java

public final class Logger 
{ 
  private static Logger logger; 
 
  private Logger() 
  { 
  } 
 
  public static synchronized Logger getInstance() 
  { 
    if ( logger == null ) 
      logger = new Logger(); 
    return logger; 
  } 
 
  public void log( String s ) 
  { 
    System.out.println( s ); 
  } 
}

Interessant sind einmal der private Konstruktor und zum anderen die Anfrage-Funktion getInstance(). Gibt es noch kein Exemplar des Loggers, erzeugt getInstance() eines und weist es der Klassenvariablen zu. synchronized schützt bei parallelen Zugriffen (dazu später in Kapitel 11 mehr).

Listing 6.36 LoggerUser.java

public class LoggerUser 
{ 
  public static void main( String[] args ) 
  { 
 
    Logger.getInstance().log( "Log mich!" ); 
  } 
}

Fabrikmethoden

Eine Fabrikmethode geht noch einen Schritt weiter als ein Singleton. Sie erzeugt nicht exakt ein Exemplar, sondern unter Umständen auch mehrere. Die grundlegende Idee jedoch ist, dass der Anwender nicht über einen Konstruktor ein Exemplar erzeugt, sondern im Allgemeinen über eine statische Funktion. Dies hat den Vorteil, dass die Funktion

  • alte Objekte aus einem Cache wiedergeben kann,
  • den Erzeugungsprozess auf Unterklassen verschieben kann und
  • null zurückgeben darf.

Ein Konstruktor erzeugt immer ein Exemplar der eigenen Klasse. Eine Rückgabe wie null kann ein Konstruktor nicht liefern, denn bei new wird immer ein neues Objekt gebaut. Fehler könnten nur über eine Exception angezeigt werden.

In der Java-Bibliothek gibt es eine Unmenge von Beispielen für Fabrikmethoden. Durch eine Namenskonvention sind sie leicht zu erkennen: Meistens heißen sie getInstance(). Eine Suche in der API-Dokumentation fördert gleich 90 solcher Funktionen hervor. Viele sind parametrisiert, um der Funktion anzuzeigen, was sie genau erzeugen soll. Nehmen wir zum Beispiel die statischen Fabrikmethoden vom java.util.Calendar:

  • Calendar.getInstance()
  • Calendar.getInstance( java.util.Locale )
  • Calendar.getInstance( java.util.TimeZone )

Die nicht parametrisierte Funktion gibt ein Standard-Calendar-Objekt zurück. Calendar ist aber selbst eine abstrakte Basisklasse, und innerhalb der Funktion befindet sich Quellcode wie der folgende:

static Calendar getInstance() 
{ 
  return new GregorianCalendar(); 
}

Im Rumpf der Erzeugerfunktion getInstance() wird bewusst die Unterklasse GregorianCalendar ausgewählt, die Calendar erweitert. Das ist möglich, da durch Vererbung eine Ist-eine-Art-von-Beziehung gilt und GregorianCalendar ein Calendar ist. Der Aufrufer von getIn-stance() bekommt das nicht mit, und er empfängt wie gewünscht ein Calendar-Objekt. Mit dieser Möglichkeit kann getInstance() testen, in welchem Land die JVM läuft, und abhängig davon die passende Calendar-Implementierung auswählen.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.






<< zurück
  Zum Katalog
Zum Katalog: Java ist auch eine Insel





Java ist auch eine Insel
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Tipp
Zum Katalog: Coding for Fun





 Coding for Fun


 Buchempfehlungen
Zum Katalog: Objektorientierte Programmierung





 Objektorientierte
 Programmierung


Zum Katalog: Einstieg in Eclipse 3.4






 Einstieg in
 Eclipse 3.4


Zum Katalog: Java 6 lernen mit Eclipse






 Java 6 lernen
 mit Eclipse


Zum Katalog: NetBeans Platform 6






 NetBeans
 Platform 6


Zum Katalog: Java und XML






 Java und XML


Zum Katalog: Visual C# 2008






 Visual C# 2008


Zum Katalog: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


Zum Katalog: C++ von A bis Z






 C++ von A bis Z


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Galileo Press 2009
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.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de