Schlagwort-Archiv: lazy initialization

Java: Best Practice – Teil I

Lazy initialization

Das Erstellen von Objekten in Java ist eine der kostspieligsten Operationen in Hinblick auf Performance und Speicherverbrauch. Aus diesem Grund sollten Objekte nur dann erzeugt werden, wenn sie auch gebraucht werden. Diese Art der Objekterzeugung wird als lazy initialization bezeichnet. Eine Möglichkeit es zu implementieren könnte beispielsweise so aussehen:

public class Mitarbeiter {

     private List mitarbeiter = null;

     public List getMitarbeiter() {

          //Liste nur initialisieren, wenn sie benötigt wird
          if (mitarbeiter == null) {
               mitarbeiter = new ArrayList()
          }
          return mitarbeiter;
     }
}

Lazy initialization ist besonders sinnvoll beim Erzeugen von Listen und Arrays. Diese Art der Objekterzeugung sollte jedoch vermieden werden, wenn das Erstellen des Objektes mit komplexeren Berechnungen verbunden ist. In so einem Fall sollte das Objekt und dessen eventuelle Unterobjekte vor dem eigentlichen Gebrauch erzeugt werden. In allen anderen Fällen sollte lazy initialization verwendet werden, um Speicher und Rechenzeit zu sparen.

Primitive Datentypen vs. Wrapper Klassen

Wrapper Klassen sind relativ neu in Java und sehr hilfreich. Java wandelt diese zur Laufzeit automatisch in ihre jeweiligen Primitiven Datentypen um und umgekehrt. Das hat zur Folge, dass Wrapper Klassen mehr und mehr eingesetzt werden. Trotzdem sollte man verneiden sie einzusetzen, wenn es nicht unbedingt notwendig ist. Ein Grund ist die Performance. Primitive Datentypen speichern lediglich einen Wert, wohingegen eine Wrapper Klasse neben dem Wert noch andere Objekteigenschaften im Speicher hält. Ein weiterer Grund ist die erhöhte Fehleranfälligkeit des Codes beim Einsatz von Wrapper Klasse.

Ein Beispiel, welches die unerwarteten Resultate beim Einsatz von Wrapper Klassen demonstriert, ist im nächsten Beispiel zu sehen:

//Primitive Datentypen
int primitiveX = 10;
int primitiveY = 10;

//Wrapper class für int
Integer wrapperX = new Integer(10);
Integer wrapperY = new Integer(10);

//Ergebnis ist wahr
System.out.println(primitiveX == primitiveY);
//Ergebnis ist falsch
System.out.println(wrapperX == wrapperY)

Der erste Vergleich liefert als Ergebnis true, da dort zwei primitive Datentypen mit dem gleichen Wert verglichen werden. Beim zweiten Vergleich ist das Ergebnis false, obwohl auch hier der Wert beider Wrapper-Objekte gleich ist. Nur werden hier keine zwei Werte verglichen, sondern die Referenzen der beiden Objekte. Dieser kleine Unterschied führt zu zwei sehr unterschiedlichen Ergebnissen und kann in einem Programm zu schwer nachvollziehbaren Fehlern führen.

Das zweite Beispiel zeigt, dass man beim Einsatz von Wrapper Klassen nicht vergessen sollte, diese zu initialisieren. Das folgende Beispiel würde nichts auf der Konsole ausgeben, sondern statt dessen eine NullPointerException werfen. Wenn man sich noch einmal ins Gedächtnis ruft, dass Wrapper Klassen Objekte sind, dann wird die Fehlerquelle sofort klar. In der if-Abfrage wird versucht auf ein nicht initialisiertes Objekt zuzugreifen, welches den Wert null besitzt und demzufolge eine NullPointerException geworfen.

Boolean test;

if(test == true || test == false) {
    System.out.println("Testvariable hat einen Wert.");
}

Konkatenierung von String-Objekten

Die Konkatenation von String-Objekten mit Hilfe des “+”-Operators in Java ist eine kostspielige Angelegenheit. Bei jeder Konkatenation erzeugt Java Kopien der zu kopierenden String-Objekte, was zu einer schlechten Performance und einem erhöhten Speicherverbraucht führt. Will man mehrere String-Objekte miteinander verbinden, so sollte man dies über die StringBuffer-Klasse machen. Auf diese Weise wird vermieden, dass unnötige Kopien erzeugt werden, sowie eine höhere Performance gewährleistet. Ein Beispiel für die Verwendung der StringBuffer-Klasse seht ihr hier:

StringBuffer strBuf = new StringBuffer();
strBuf.append("Auf diese Weise");
strBuf.append("lassen sich Zeichenketten");
strBuf.append("ressourcen schonender Verbinden.");
System.out.println(strBuf.toString());

Leere Collections und Arrays

Die Klasse java.util.Collections besitzt einige Hilfreiche Methoden, die einem das Leben bei der Arbeit mit Collections und Arrays erleichtern. Besonders bei Methoden, die eine Collection oder ein Array zurückliefern, machen die Hilfsmethoden Sinn. Man sollte vermeiden bei solchen Methoden null zurückzugeben. Statt dessen sollte eine leere Collection bzw. ein leeres Array zurückkommen. Auf diese Weise muss beim Aufruf der Methode nicht überprüft werden, ob null zurückgegeben wurde. Man spart sich also eine Überprüfung des Rückgabewertes auf null und vermeidet zusätzlich eine weitere Fehlerquelle, die eventuell zu einer NullPointerException geführt hätte. Zwei Hilfsmethoden der Collections-Klasse sind unten aufgeführt. Sie liefern jeweils automatisch den korrekten Typ der Collection zurück.

public List getNameList() {
     if (nameList == null)
          return Collections.emptyList();
     return nameList;
}

public HashMap getNameHashMap() {
     if (nameHashMap == null)
          return Collections.emptyMap();
     return nameHashMap;
}