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。