MD.*_*oob 13 java puzzle 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);
}
}
Run Code Online (Sandbox Code Playgroud)
如果我们将主要功能更改"Hello World"为new String("Hello World"):
System.out.println(new String("Hello World"));
Run Code Online (Sandbox Code Playgroud)
它输出Hello world.
实际上发生了什么?
MD.*_*oob 17
这个源代码打开了一些有趣的java技术.让我们逐一检查.
首先,我们需要了解代码的流程.代码的哪一部分将首先执行?
静态初始化块.为什么?让我们参考Java语言规范(12.4):
类的初始化包括执行其静态初始化程序和类中声明的静态字段(类变量)的初始化程序.
它什么时候发生?再次来自JLS(12.4.1):
T是一个类,并且调用由T声明的静态方法.
因此我们可以得出结论,静态initiazlier将在main方法之前首先执行.
现在,这两行正在使用反射:
Field value = String.class.getDeclaredField("value");
value.setAccessible(true);
Run Code Online (Sandbox Code Playgroud)
为简单起见,我们可以将第一行分成两行:
Class<String> c = String.class;
Field value = c.getDeclaredField("value");
Run Code Online (Sandbox Code Playgroud)
第一行是检索Reflected Class Object,第二行是检索Field表示类的value字段的行String.
value.setAccessible(true)表示反射的类对象在使用时应禁止Java语言访问检查.(参考).
问题的下一行是
value.set("Hello World", value.get("G'Day Mate."));
Run Code Online (Sandbox Code Playgroud)
如果我们深入了解.set()文档,我们可以看到我们正在调用它的set(Object aObject,Object value)版本set.value.get("G'Day Mate.")返回"G'Day Mate."的value字段的值实际上是一个char[].并且通过调用set它将"Hello World"对象的值字段的值替换为"G'Day Mate."对象的值字段.
该static块的代码进行了说明.
让我们潜入主要功能.这很简单.它应该输出Hello, world.但它正在输出G'Day Mate.为什么?因为Hello, world我们在static初始化程序中创建的String对象与Hello, world我们在main函数中使用的对象相同.再次咨询JLS将会对此有所了解
此外,字符串文字始终引用类String的相同实例.这是因为字符串文字 - 或者更常见的是作为常量表达式(第15.28节)的值的字符串 - 被"实例化"以便使用String.intern方法共享唯一实例.
这个答案可以帮助您更简洁地理解事实.
所以它显示了不同的值,因为我们已经将Hello,world对象的值更改为G'Day, Mate.
但是如果你new String("Hello world")在main函数中使用它将直接创建一个新的实例,String而不是检查它的池.因此Hello world,main函数Hello world与我们更改了值的静态初始化程序不同.
由于new String("Hello World")在堆创建新的对象,而不是使用以前创建"Hello World"的字符串常量池的对象.
所以,如果你写
System.out.print(new String("Hello World").intern());
它会显示输出为G'Day, Mate.因为intern()方法返回一个字符串实例的引用id String Constant Pool.