Java拼图与reflection和字符串

这个源输出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); } } 

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

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

它输出Hello world

实际上发生了什么?

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

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

静态初始化块。 为什么? 让我们参考Java语言规范(12.4) :

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

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

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

因此我们可以得出结论,静态initiazlier将在main方法之前首先执行。

现在,这两行使用reflection:

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

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

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

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

value.setAccessible(true)表示reflection的类对象在使用时应禁止Java语言访问检查。( 参考 )。

问题的下一行是

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

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

解释static块的代码。

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

此外,字符串文字始终引用类String的相同实例。 这是因为字符串文字 – 或者更常见的是作为常量表达式(第15.28节)的值的字符串 – 被“实例化”以便使用String.intern方法共享唯一实例。

这个答案可以帮助您更简洁地理解事实。

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

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

new String("Hello World")在堆中创建新的Object而不是在String Constant Pool中使用先前创建的"Hello World"对象。

所以,如果你写

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

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