Вопрос по reflection, string, java – Изменяемые строки в Java

11

Как почти все знают, строки в 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 принцип?

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

Ваш Ответ

1   ответ
17

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

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

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

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

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

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

Похожие вопросы