4.10.4 | Kapselung von Konstruktoren |
Es gibt Fälle, in denen man verhindern möchte, dass Benutzer einer Klasse Exemplare direkt über die Konstruktoren erzeugen. Die beiden folgenden Fälle sind die wohl wichtigsten und auch oft in der Standardbibliothek zu finden:Um die direkte Erzeugung von Exemplaren zu verhindern, darf kein Konstruktor von »außen« zugreifbar sein, auch der implizite Konstruktor nicht. Es genügt also nicht, einfach in der Klasse keine Konstruktoren vorzusehen, da der implizite Konstruktor dann immer noch verfügbar ist. Um diesen gewissermaßen zu verstecken, muss er mit der Zugriffsklasse private überschrieben werden:
- Bei Klassen, die oft benötigte Hilfsroutinen bereitstellen, wird häufig so vorgegangen, dass alle Routinen als statische Methoden implementiert werden. Das heißt, zur Verwendung der Methoden ist keine vorhergehende Erzeugung eines Exemplars notwendig. Um dies zu unterstreichen, kann man die Erzeugung von Exemplaren vollständig unterbinden, so dass nur noch die statischen Methoden aufrufbar sind.
Entsprechende Beispiele aus der Standardbibliothek sind die Klasse java.lang.Math, die mathematische Funktionen bereitstellt oder auch java.util.Arrays, mit der Arrays sortiert oder auch gefüllt werden können. Die Funktionalität dieser Klassen ist ausschließlich mit statischen Methoden realisiert, so dass kein Exemplar nötig ist, um diese zu verwenden.
- Man möchte sicherstellen, dass es von einer Klasse insgesamt nur ein einziges Exemplar gibt (so genanntes Singleton, vgl. Gamma, »Entwurfsmuster«). Ein Beispiel für diesen Fall ist die Klasse Runtime im Paket java.lang. Sie repräsentiert die Laufzeitumgebung eines Programms, die allen Threads und Programmen in einer Virtual Machine gemeinsam ist. Entsprechend macht es Sinn, dass es auch nur ein Objekt gibt, das diese Laufzeitumgebung und ihren Zustand darstellt.
private MyClass() {}Wenn dies der einzige Konstruktor der Klasse MyClass ist, können von außerhalb der Klasse keine Exemplare mehr erzeugt werden. Wie das Beispiel zeigt, ist es nicht unüblich, dass der Konstruktor in diesem Fall einen leeren Rumpf hat. Insbesondere im ersten der beiden oben genannten Fälle macht dies Sinn, weil die gesamte Funktionalität in statischen Methoden enthalten ist.
Im zweiten Anwendungsfall mag es dagegen angebracht sein, Initialisierungsaktionen im privaten Konstruktor für das Singleton zu implementieren. Das gesamte Singleton-Muster hat folgende Struktur:public class SingletonDemo { private static SingletonDemo singleton; // Der leere Konstruktor private SingletonDemo() {} // Liefert das Singleton-Exemplar zurück public static SingletonDemo getInstance() { // Nur beim ersten Aufruf der Methode // ein neues Exemplar erzeugen if (singleton == null) singleton = new SingletonDemo(); return singleton; } }