Подтвердить что ты не робот

Java-головоломка с отражением и строкой

Этот источник выводит 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.

Что происходит на самом деле?

4b9b3361

Ответ 1

Этот исходный код открывает некоторые интересные методы Java. Давайте рассмотрим один за другим.

Сначала нам нужно понять поток кода. Какая часть кода будет выполнена в первую очередь?

Статический блок инициализации. Зачем? Проконсультируйтесь с Спецификация языка Java (12.4):

Инициализация класса состоит в выполнении его статических инициализаторов и инициализаторов для статических полей (переменных класса), объявленных в классе.

И когда это происходит? Снова из JLS (12.4.1):

T - класс, и статический метод, объявленный T, вызывается.

Итак, мы можем прийти к выводу, что статический initiazlier будет выполняться первым перед основным методом.

Теперь эти две строки используют отражение:

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

Для простоты мы можем разбить первую строку на две строки:

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

Первая строка возвращает Reflected Class Object, а вторая строка возвращает Field, которая представляет поле value класс String.

value.setAccessible(true) указывает, что объект отраженного класса должен подавлять проверку доступа к языку Java, когда он используется. (Ссылка).

Следующая строка под вопросом

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

Если мы погрузимся в . set() documenation, мы увидим, что мы вызываем 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 String, который мы создали в инициализаторе static, тот же, что и объект Hello, world, который мы используем в основной функции. Консультирование с JLS снова проливает свет на него

Кроме того, строковый литерал всегда ссылается на тот же экземпляр класса String. Это связано с тем, что строковые литералы, или, в более общем смысле, строки, которые являются значениями константных выражений (§15.28), "интернированы", чтобы обмениваться уникальными экземплярами, используя метод String.intern.

Этот ответ может помочь вам понять факт более кратко.

Таким образом, он показывает другое значение, поскольку мы уже изменили значение объекта Hello,world на G'Day, Mate.

Но если вы используете new String("Hello world") в основной функции, он будет непосредственно создавать новый экземпляр String, а не проверять его пул. Таким образом, Hello world основной функции будет отличаться от Hello world статического инициализатора, из которого мы изменили значение.

Ответ 2

Так как new String("Hello World") создает новый объект в куче, а не использует ранее созданный объект "Hello World" в String Constant Pool.

Итак, если вы пишете

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

он отобразит вывод как G'Day, Mate. потому что метод intern() возвращает ссылочный идентификатор экземпляра строки из String Constant Pool.