Изменяемые строки в Java

Как почти все знают, строки в Java неизменны. Недавно я обнаружил кое-что, что могло бы предположить, что это не всегда верно. Давайте попробуем этот код:

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);

результат будет:

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

Как работает этот трюк? Как JVM знает, какие объекты следует изменить, а какие нет? Какой механизм есть под капотом этого трюка? Почему уже созданоbeforeTest Строка не была изменена? Этот трюк действительно умаляетstrings are immutable принцип?

 Dave Newton23 июн. 2012 г., 00:05
@MikeSamuel Reflectionitself четко определен.Using это не так, поэтому начинается вуду, когда начинается грязное дело. У меня есть целая основа для этого (Muckito).
 Hovercraft Full Of Eels23 июн. 2012 г., 00:00
Отражение черной магии вуду.
 Mike Samuel23 июн. 2012 г., 00:01
@HovercraftFullOfEels, Reflection прекрасно определен. Это только когда вы нарушаетеprivate позвонив по телефонуsetAccessible что инварианты основного класса выходят в окно.

Ответы на вопрос(1)

Решение Вопроса

Строковые литералы интернированы в пул. Это означает, что когда вы пишете

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

s1 и s2 ссылаются на один и тот же объект String, а s3 ссылается на другой объект, поддерживаемый другим массивом символов.

В своем коде вы нарушаете инварианты String, изменяя закрытый массив символов, содержащий символы & quot; Original & quot; Строковый буквальный экземпляр. Но с тех порbeforeTest ссылается на другой экземпляр String, он не изменен.

Неизменность достигается за счет того, что поля остаются частными в объекте, и не предоставляются какие-либо методы для изменения этого частного состояния Используя рефлексию, вы нарушаете все правила инкапсуляции и тем самым можете нарушать неизменность.

 23 июн. 2012 г., 00:14
Разве третий экземпляр (ссылка на тест) также не указывает на новый экземпляр? Если & quot; Оригинал & quot; буквенный символ заменяется на «измененный»; не следует ли обновлять значение beforeTest (потому что, например, передается и тот же литерал)?
 23 июн. 2012 г., 00:16
test переменная инициализируется как копия «оригинала» Строка буквальная. Но вы уже изменили его содержание на "Изменено" когда копия сделана. Так что это копия "Модифицированной".beforeTest также является копией «оригинала»; Строковый литерал, но копия была сделана до того, как вы изменили ее содержимое.

Ваш ответ на вопрос