Java拼图与反射和字符串

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与我们更改了值的静态初始化程序不同.


Akh*_*bey 5

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

所以,如果你写

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

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