© Excellent backgrounds/Shutterstock.com
Kolumne: Java-Trickkiste

Kolumne: Java-Trickkiste


Wenn ein Feld einer Klasse final ist, muss man es im Konstruktor initialisieren. Danach behält es seinen Wert, solange das Objekt lebt. Oder doch nicht? Dieser Artikel betrachtet die Möglichkeiten, nachträglich die Werte von Feldern zu ändern, die als final deklariert sind. Man kann damit wilden Schabernack treiben. Es gibt aber auch sinnvolle Anwendungsbereiche dafür. Aber fangen wir mit der skurrilen Seite des Themas an.

Hallo – und Tschüs

Der Code in Listing 1 ruft zuerst eine Methode auf, die unbekannte Dinge tut. Anschließend gibt sie zur Begrüßung den Text "Hallo" aus. Was wirklich auf der Konsole erscheint, hängt aber überraschenderweise davon ab, was die Methode magic() tut. Stringkonstanten sind in Java nicht ganz so konstant, wie man das landläufig erwartet.

Listing 2 zeigt eine Methode magic(), die aus der Stringkonstanten "Hallo" den String "Tschüs" macht – und zwar überall im Programm. Damit gibt der Code in Listing 1 "Tschüs" aus, obwohl im Quelltext "Hallo" steht.

Listing 1

magic(); System.out.println("Hallo");

Listing 2

static void magic() throws Exception { final Field field = String.class.getDeclaredField("value"); field.setAccessible(true); field.set("Hallo", "Tschüs".toCharArray()); }

Das passiert per Reflection. Die Klasse String speichert ihre Zeichenkette in einem privaten char Array mit dem Namen value. Es gibt keine Garantie, dass das immer so bleibt, schließlich ist das Feld privat – aber in Java 1.7 ist String so implementiert.

Das Feld ist privat, und deshalb ruft der Code getDeclaredField auf der String-Klasse auf, weil diese Methode auch versteckte Felder liefert. Per Default erzwingt sie die Sichtbarkeitsregeln für Felder, auch wenn der Zugriff per Reflection erfolgt, verhindert also Zugriff auf private Felder.

Bevor unser Code das Feld verändern kann, muss er das ausschalten. Dazu ruft er setAccessible(true) auf. Nach diesen Vorbereitungen kann die Methode jetzt den Wert des Felds value ändern. Das tut sie auf dem String-Literal "Hallo" und ersetzt dessen Inhalt durch die Zeichenfolge "Tschüs". Das funktioniert, weil man in Java per Reflection auch final Felder verändern kann.

String.intern()

Aber warum verändert das den Wert von "Hallo" an völlig anderen Stellen im Programm? Der Grund liegt darin, dass die JVM alle Stringkonstanten schon beim Laden der Klassen in einem globalen Pool ablegt, um Speicher zu sparen. Jeder String-Literal belegt dadurch nur einmal Speicher, egal in wie vielen Klassen es vorkommt.

Wenn also di...

Neugierig geworden?

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang