我正在尝试找到this question的第三个解决方案。
我不明白为什么这不打印false
。
public class MyClass {
public MyClass() {
try {
Field f = String.class.getDeclaredField("value");
f.setAccessible(true);
f.set("true", f.get("false"));
} catch (Exception e) {
}
}
public static void main(String[] args) {
MyClass m = new MyClass();
System.out.println(m.equals(m));
}
}
当然,由于字符串内部操作,正在修改的
"true"
实例与print
的PrintStream
方法中使用的实例完全相同。public void print(boolean b) {
write(b ? "true" : "false");
}
我想念什么?
编辑
@yshavit有趣的一点是,如果您添加该行
System.out.println(true);
在
try
之前,输出为true
false
最佳答案
可以说这是HotSpot JVM错误。
问题出在字符串文字内联机制中。
字符串常量的
java.lang.String
实例是在常量池解析期间延迟创建的。 CONSTANT_String_info
的 CONSTANT_Utf8_info
结构表示的。 MyClass
和PrintStream
对于字面量“true”具有自己的一对CONSTANT_String_info
/CONSTANT_Utf8_info
cpool条目。 CONSTANT_String_info
时,JVM会启动解析过程。字符串实习是此过程的一部分。 CONSTANT_Utf8_info
的内容与StringTable
中的字符串实例的内容进行比较。 char[]
数组内容进行比较,该内容可以由用户通过Reflection欺骗。 那么,您的测试中发生了什么?
f.set("true", f.get("false"))
启动MyClass
中文字“true”的解析。 StringTable
中没有发现与序列“true”匹配的实例,并创建了一个新的java.lang.String
,该实例存储在StringTable
中。 value
中该字符串的StringTable
通过反射替换。 System.out.println(true)
启动PrintStream
类中文字“true”的解析。 StringTable
中的字符串进行比较,但未找到匹配项,因为该字符串已经具有“false”值。创建另一个“true”字符串,并将其放置在StringTable
中。 为什么我认为这是一个错误?
JLS §3.10.5和JVMS §5.1要求包含相同字符序列的字符串文字必须指向
java.lang.String
的相同实例。但是,在下面的代码中,使用相同的字符序列对两个字符串文字进行解析会导致不同的实例。
public class Test {
static class Inner {
static String trueLiteral = "true";
}
public static void main(String[] args) throws Exception {
Field f = String.class.getDeclaredField("value");
f.setAccessible(true);
f.set("true", f.get("false"));
if ("true" == Inner.trueLiteral) {
System.out.println("OK");
} else {
System.out.println("BUG!");
}
}
}
JVM的一种可能解决方法是将指向原始UTF序列的指针与
StringTable
对象一起存储在java.lang.String
中,以便内部处理过程不会将cpool数据(用户无法访问)与value
数组(可通过反射访问)进行比较。