以下课程的输出让我感到震惊,但我不明白它会如何发生。
public class SampleTest {
public static void main(String[] args) throws SecurityException, NoSuchFieldException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException {
String someString = "IMMUTABLE";
Field field = Class.forName("java.lang.String").getDeclaredField("value");
field.setAccessible(true);
char[] value = (char []) field.get(someString);
String anotherString = "NOTREALLY";
for (int i=0; i<value.length; i++){
char c = anotherString.toCharArray()[i];
value[i]=c;
}
System.out.println(someString); // prints NOTREALLY
System.out.println("IMMUTABLE"); // Why it prints NOTREALLY here..!!!
}
}
答案 0 :(得分:0)
TL; DR:不要这样做。 该代码是(ab)使用反射来违反规范。根据JLS,String
values cannot change。该代码通过访问其未记录的私有内部结构来更改字符串的值。不好的主意。
发生了什么:
类中的等价字符串文字全部组合在一起,因此在运行时,它们都引用内存中相同的字符串对象;他们被写入班级的“恒定池”。因此,该类中出现的所有"IMMUTABLE"
实际上都引用了String
的同一个实例。这意味着实际上是最后一行
System.out.println("IMMUTABLE");
......相当于
System.out.println(someString);
...因为一旦加载了类,文字和变量就会引用同一个对象。
由于代码(ab)使用反射来覆盖字符串对象的私有未记录值,因此您自然会在使用该对象的所有位置看到更新状态。
如果您将System.out.println("IMMUTABLE");
行移到单独的类中并确保在修改代码 之前已加载该类,那么也会发生这种情况someString
。这是因为当加载一个类时,其常量池中的字符串是interned,因此不同类中的等效字符串常量最终会引用相同的String
对象。这不是你想要依赖的东西,尤其是因为字符串可以(这些天)从实习池中被淘汰出来,但在一个简单的例子中你可能会观察它。
例如,假设您有这个单独的类:
public class Separate {
public static void show() {
System.out.println("IMMUTABLE");
}
}
您修改了代码以使用它,但只有在修改someString
之后:
public static void main(String[] args) throws SecurityException, NoSuchFieldException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException {
String someString = "IMMUTABLE";
// ...
System.out.println(someString); // prints NOTREALLY
Separate.show();
}
你会得到
NOTREALLY IMMUTABLE
...因为在加载Separate
时,您已经修改了实习池中字符串的版本,因此当我们加载Separate
时,它不匹配并最终引用不同的String
对象。
但是,如果您确保在之前加载了 ,则修改它:
Separate
你得到了一点乐趣:
IMMUTABLE NOTREALLY NOTREALLY
...因为加载public static void main(String[] args) throws SecurityException, NoSuchFieldException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException {
Separate.show(); // <================ Note
String someString = "IMMUTABLE";
// ...
System.out.println(someString); // prints NOTREALLY
Separate.show();
}
时,其Separate
等同于实习池中的"IMMUTABLE"
,因此Separate
使用String
实例 - 然后,main
中的代码会修改。