小编典典

字符串文字,内部和反射

java

我正在尝试找到这个问题的第三种解决方案。

我不明白为什么这个不打印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

阅读 208

收藏
2020-10-25

共1个答案

小编典典

可以说这是HotSpot JVM错误。

问题在于字符串字面量的内部机制

  • java.lang.String 在常量池解析期间,将懒惰地创建字符串文字的实例。
  • 最初,字符串常量在常量池中由CONSTANT_String_info指向的结构表示CONSTANT_Utf8_info
  • 每个类都有自己的常量池。也就是说,MyClass而且PrintStream有自己的一双CONSTANT_String_info/ CONSTANT_Utf8_infoCPOOL条目文字 “真”
  • CONSTANT_String_info首次被访问时,JVM启动解决进程。字符串实习是此过程的一部分。
  • 为了找到被禁派文字的匹配项,JVM将的内容CONSTANT_Utf8_info与中的字符串实例的内容进行比较StringTable
  • ^^^这就是问题所在。将来自cpool的原始UTF数据与Java char[]数组内容进行比较,该内容可以由用户通过Reflection欺骗。

那么,您的测试中发生了什么?

  1. f.set("true", f.get("false"))启动字面的决议 “真”MyClass
  2. JVM在StringTable匹配序列 “ true”时 未发现任何实例,并创建了一个新实例,并将java.lang.String其存储在中StringTable
  3. value该字符串来自的字符串StringTable通过反射替换。
  4. System.out.println(true)在类中启动字面量 “ true” 的解析PrintStream
  5. 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序列的指针StringTablejava.lang.String对象一起存储,以便内部处理过程不会将cpool数据(用户无法访问)与value数组(可通过Reflection访问)进行比较。

2020-10-25