【问题标题】:Java Puzzle with reflection and String带有反射和字符串的 Java 拼图
【发布时间】:2013-11-30 22:23:51
【问题描述】:

这个源输出G'Day Mate.这是怎么发生的?

public static void main(String args[]) {
    System.out.println("Hello World");
}

static {
    try {
        Field value = String.class.getDeclaredField("value");
        value.setAccessible(true);
        value.set("Hello World", value.get("G'Day Mate."));
    } catch (Exception e) {
        throw new AssertionError(e);
    }
}

如果我们将主函数 "Hello World" 更改为 new String("Hello World")

System.out.println(new String("Hello World"));

它输出Hello world

究竟发生了什么?

【问题讨论】:

    标签: java reflection puzzle


    【解决方案1】:

    这个源代码打开了一些有趣的java技术。让我们一一检查。

    首先我们需要了解代码的流程。哪部分代码会先执行?

    静态初始化块。为什么?欢迎咨询Java Language Specification (12.4)

    类的初始化包括执行其静态初始化程序和类中声明的静态字段(类变量)的初始化程序。

    什么时候发生?再次来自JLS (12.4.1)

    T是一个类,调用了T声明的静态方法。

    所以我们可以得出这样的结论:static initiazlier 会在 main 方法之前先执行。

    现在,这两行正在使用反射:

    Field value = String.class.getDeclaredField("value");
    value.setAccessible(true);
    

    为简单起见,我们可以将第一行分成两行:

    Class<String> c = String.class;
    Field value = c.getDeclaredField("value");
    

    第一行检索Reflected Class Object,第二行检索代表String类的value字段的Field

    value.setAccessible(true) 表示反射类对象在使用时应禁止 Java 语言访问检查。(Reference)。

    下一行是

    value.set("Hello World", value.get("G'Day Mate."));
    

    如果我们深入了解.set() documenation,我们可以看到我们正在调用setset(Object aObject,Object value) 版本。 value.get("G'Day Mate.") 正在返回 "G'Day Mate."value 字段的值,它实际上是 char[]。并且通过set 的调用,它将"Hello World" 对象的值字段的值替换为"G'Day Mate." 对象的值字段。

    解释了static 块的代码。

    让我们深入了解主要功能。 这很简单。它应该输出Hello, world。但它正在输出G'Day Mate。为什么? 因为我们在static 初始化器中创建的Hello, world String 对象与我们在主函数中使用的Hello, world 对象相同。再次咨询JLS 会有所了解

    此外,字符串字面量总是引用 String 类的同一个实例。这是因为字符串字面量 - 或者更一般地说,作为常量表达式值的字符串(第 15.28 节) - 是“内部的”,以便使用 String.intern 方法共享唯一实例。

    这个answer可以帮助你更简洁地理解事实。

    所以它显示了不同的值,因为我们已经将 Hello,world 对象的值更改为 G'Day, Mate

    但是,如果您在 main 函数中使用 new String("Hello world"),它将直接创建一个新的 String 实例,而不是检查到它的池中。所以 Hello world 的 main 函数将不同于 Hello world 的静态初始化器,我们已经改变了它的值。

    【讨论】:

    • Field value=getDeclaredField("value") 你的意思是Field value = c.getDeclaredField("value") 我不明白你为什么问和回答这个问题。
    • @OrelEraki 是的,这是一个错字。 Ans关于询问和回答,以备将来参考
    • @MD.SahibBinMahboob 当我在 java7 中执行代码时,我发现下面给出的所有三个语句的输出都与G'Day, Mate 相同。 System.out.println("Hello World"); System.out.println(new String("Hello World").intern()); System.out.println(new String("Hello World")); 那么,为什么new String("Hello World") 将输出显示为G'Day, Mate
    【解决方案2】:

    由于new String("Hello World") 在堆中创建新对象,而不是使用先前在字符串常量池中创建的"Hello World" 对象。

    所以,如果你写

    System.out.print(new String("Hello World").intern());

    它将显示输出为G'Day, Mate。因为intern() 方法返回来自String Constant Pool 的字符串实例的引用ID。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-19
      • 2011-08-25
      • 1970-01-01
      • 2011-01-16
      • 2016-01-12
      • 2013-03-19
      • 1970-01-01
      相关资源
      最近更新 更多