© Excellent backgrounds/Shutterstock.com
Java Magazin
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.

Arno Haase


Hallo – und TschüsListing 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 1magic();System.out.println("Hallo");magic();System.out.println("Hallo");static void magic() throws Exception { final Field field = String.class.getDeclaredField("value"); field.setAccessible(true); field.set("Hallo", "Tschüs".toCharArray());}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()Wenn also die magic()-Methode in Listing 2 den Wert des Strings "Hallo" verändert, betrifft das gleichzeitig alle anderen Stellen in der JVM, die schon den gleichen String-Literal verwenden. Strings, die erst zur Laufzeit erstellt werden – z. B. mit einem StringBuilder – landen nicht automatisch in diesem globalen Pool.Man kann aber mit der Methode String.intern() einen String ausdrücklich in den Pool schieben. Sie liefert einen String zurück, der dieselbe Zeichenkette enthält und garantiert im Stringpool liegt.Listing 3 erzeugt mit einem StringBuilder eine Stringinstanz von "Hallo", die nicht im Pool liegt, und erzeugt daraus durch Aufruf von intern() eine Kopie. Weil magic() auf dem globalen Pool operiert, behält die mit StringBuilder erzeugte Instanz ihren Wert. Die mit intern() erzeugte Kopie wird dagegen zu "Tschüs".Listing 3final String s1 = new StringBuilder("Hallo").toString();final String s2 = s1.intern();assert(s1 != "Hallo");assert(s2 == "Hallo");magic();System.out.println(s1); // "Hallo"System.out.println(s2); // "Tschüs"final String s1 = new StringBuilder("Hallo").toString();final String s2 = s1.intern();assert(s1 != "Hallo");assert(s2 == "Hallo");magic();System.out.println(s1); // "Hallo"System.out.println(s2); // "Tschüs"Dieses Array kann man per Reflection verändern (Listing 4). Es liegt in der...

Java Magazin
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.

Arno Haase


Hallo – und TschüsListing 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 1magic();System.out.println("Hallo");magic();System.out.println("Hallo");static void magic() throws Exception { final Field field = String.class.getDeclaredField("value"); field.setAccessible(true); field.set("Hallo", "Tschüs".toCharArray());}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()Wenn also die magic()-Methode in Listing 2 den Wert des Strings "Hallo" verändert, betrifft das gleichzeitig alle anderen Stellen in der JVM, die schon den gleichen String-Literal verwenden. Strings, die erst zur Laufzeit erstellt werden – z. B. mit einem StringBuilder – landen nicht automatisch in diesem globalen Pool.Man kann aber mit der Methode String.intern() einen String ausdrücklich in den Pool schieben. Sie liefert einen String zurück, der dieselbe Zeichenkette enthält und garantiert im Stringpool liegt.Listing 3 erzeugt mit einem StringBuilder eine Stringinstanz von "Hallo", die nicht im Pool liegt, und erzeugt daraus durch Aufruf von intern() eine Kopie. Weil magic() auf dem globalen Pool operiert, behält die mit StringBuilder erzeugte Instanz ihren Wert. Die mit intern() erzeugte Kopie wird dagegen zu "Tschüs".Listing 3final String s1 = new StringBuilder("Hallo").toString();final String s2 = s1.intern();assert(s1 != "Hallo");assert(s2 == "Hallo");magic();System.out.println(s1); // "Hallo"System.out.println(s2); // "Tschüs"final String s1 = new StringBuilder("Hallo").toString();final String s2 = s1.intern();assert(s1 != "Hallo");assert(s2 == "Hallo");magic();System.out.println(s1); // "Hallo"System.out.println(s2); // "Tschüs"Dieses Array kann man per Reflection verändern (Listing 4). Es liegt in der...

Neugierig geworden?


   
Loading...

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