2012-06-22 12 views
11

Jak prawie każdy wie, struny w Javie są niezmienne. Ostatnio odkryłem coś, co może sugerować, że nie zawsze jest to prawda. Spróbujmy się ten kod:Zmutowane struny w Javie

System.out.println("-------- BEFORE MODIFICATIONS --------"); 
String beforeTest = new String("Original"); 
System.out.println(beforeTest); 
java.lang.reflect.Field valueField = String.class.getDeclaredField("value"); 
valueField.setAccessible(true); 
valueField.set("Original", "Modified".toCharArray()); 
System.out.println("-------- AFTER MODIFICATIONS --------"); 
System.out.println(beforeTest); 
System.out.println("Original"); 
String test = new String("Original"); 
System.out.println(test); 
String test2 = new String("Original 2"); 
System.out.println(test2); 

wyjście byłoby:

-------- BEFORE MODIFICATIONS -------- 
Original 
-------- AFTER MODIFICATIONS -------- 
Original 
Modified 
Modified 
Original 2 

Jak to działa sztuczka? W jaki sposób JVM wie, które obiekty powinny zostać zmienione, a które nie? Jaki mechanizm znajduje się pod maską tej sztuczki? Dlaczego utworzony ciąg beforeTest nie został zmieniony? Czy ta sztuczka rzeczywiście stanowi odstępstwo od zasady strings are immutable?

+6

Odbicie to czarna magia voodoo. –

+1

@HovercraftFullOfEels, Reflection jest doskonale zdefiniowany. Dopiero gdy naruszasz 'private' przez wywołanie' setAccessible', to niezmienniki klasy core wychodzą przez okno. –

+3

@MikeSamuel Reflection * sama * jest dobrze zdefiniowana. * Używanie * nie jest, dlatego voodoo raz głupi zaczyna się niemożliwie. Mam do tego całe ramy (Muckito). –

Odpowiedz

17

Literały łańcuchowe są internowane w puli. Oznacza to, że kiedy piszesz

String s1 = "Foo"; 
String s2 = "Foo"; 
String s3 = new String("Foo"); 

S1 i S2 odnoszą się do tego samego obiektu String i s3 odnosi się do innego, wspierany przez innego tablicy char.

W swoim kodzie naruszysz niezmienniki String, modyfikując prywatną tablicę znaków, która zawiera znaki z literału "Oryginał". Ale ponieważ beforeTest odnosi się do innej instancji String, nie jest modyfikowana.

Niemożność uzyskuje się, zachowując pola prywatne w obiekcie i nie udostępniając żadnej metody modyfikacji tego stanu prywatnego. Używając refleksji, łamiesz wszystkie zasady enkapsulacji, a tym samym możesz naruszyć niezmienność.

+0

Czy trzecia instancja (odwołanie do testu) nie wskazuje również na nową instancję? Jeśli "Oryginalny" literowy znak zastępuje się "Zmodyfikowany", to czy wartość przed aktualizacją nie powinna zostać zaktualizowana (ponieważ ten sam literał jest również przekazywany na przykład)? – kosa

+0

Zmienna "test" jest inicjalizowana do kopii literału "Oryginał". Ale użytkownik już zaktualizował swoją zawartość do "Zmodyfikowanej" podczas tworzenia kopii. To jest kopia "Zmodyfikowanej". 'beforeTest' jest także kopią literału" Original ", ale kopia została wykonana przed zmodyfikowaniem jej treści. –