我正在尝试找到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"实例与printPrintStream方法中使用的实例完全相同。
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 结构表示的。
  • 每个类都有自己的常量池。也就是说,MyClassPrintStream对于字面量“true”具有自己的一对CONSTANT_String_info/CONSTANT_Utf8_info cpool条目。
  • 首次访问CONSTANT_String_info时,JVM会启动解析过程。字符串实习是此过程的一部分。
  • 为了找到要被禁派的文字的匹配项,JVM将CONSTANT_Utf8_info的内容与StringTable中的字符串实例的内容进行比较。
  • ^^^这就是问题所在。将来自cpool的原始UTF数据与Java char[]数组内容进行比较,该内容可以由用户通过Reflection欺骗。

  • 那么,您的测试中发生了什么?
  • f.set("true", f.get("false"))启动MyClass中文字“true”的解析。
  • JVM在StringTable中没有发现与序列“true”匹配的实例,并创建了一个新的java.lang.String,该实例存储在StringTable中。
  • value中该字符串的
  • StringTable通过反射替换。
  • System.out.println(true)启动PrintStream类中文字“true”的解析。
  • JVM将utf序列“true”与StringTable中的字符串进行比较,但未找到匹配项,因为该字符串已经具有“false”值。创建另一个“true”字符串,并将其放置在StringTable中。

  • 为什么我认为这是一个错误?

    JLS §3.10.5JVMS §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数组(可通过反射访问)进行比较。

    10-08 09:33
    查看更多