Как работает этот статический код?

код:

public static void main ( String[] args){
      String a = new String("Hello");
      String b = " pardner.";
      System.out.println(a+b);
      System.out.println("a.equals(\"Hello\") --> " + (a.equals("Hello")));
      System.out.println("a --> " + a);
}

static {
      try {
          Field value = String.class.getDeclaredField("value");
          value.setAccessible(true);
          value.set("Hello", value.get("Howdy"));
      } catch (Exception e) { }
}

Результат:

Howdy pardner.
a.equals("Hello") --> true
a --> Howdy

Как этот код меняет "Hello" на "Howdy" при печати?

Ответы

Ответ 1

Во-первых, String литералы, состоящие из одних и тех же символов, разрешают один и тот же экземпляр. Так что в

String one = "hello";
String two = "hello";

обе переменные ссылаются на один и тот же объект.

Во-вторых, static блоки инициализатора выполняются, когда класс сначала загружается (и инициализируется). Это происходит до вызова любых методов класса, т.е. до main.

В-третьих, ваша реализация версии Java String, предположительно, использует поле char\[\] для хранения строки символов. Это поле называется value.

Вы используя отражение для извлечения этого char[] для объекта String, на который ссылается String literal "Howdy".

Field value = String.class.getDeclaredField("value");
...
value.get("Howdy")

и присваивая его полю char[] объекта String, на который ссылается String literal "Hello"

value.set("Hello", value.get("Howdy"));

Теперь, когда ваш main метод выполняет

String a = new String("Hello");

Литерал String "Hello" ссылается на тот же объект, для которого вы ранее задали поле char[]. Этот char[] содержит символы 'H', 'o', 'w', 'd' и 'y', поскольку он был взят из объекта String, на который ссылается литерал "Howdy".

Эти символы копируются в созданный здесь новый String объект

String a = new String("Hello"); 

Ответ 2

Первое, что происходит, - это ваш блок static. При отражении он меняет значение String a на "Howdy" (на самом деле он меняет значение String "Hello" на "Howdy", но имеет тот же эффект). Однако вы получаете

a.equals("Hello") --> true

потому что компилятор уже заменил значение true. Я побежал javap -v и получил

31: ldc           #75                 // String a.equals(\"Hello\") --> true

Итак, это именно то, что произошло. Как я заметил в комментариях, если вы измените String a на

final String a = "Hello";

Последняя строка изменяется на

a --> Hello

по той же причине.

Ответ 3

Взгляните сюда:

http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Field.html

public void set (Object obj, Object value)

Устанавливает поле, представленное этим объектом Field, по указанному аргумент объекта указанному новому значению. Новое значение автоматически разворачивается, если базовое поле имеет примитивный тип.

public get (Object obj)

Возвращает значение поля, представленного этим полем, на указанного объекта.

Ваш статический блок выполняется в первую очередь.

Все вхождения строки, содержащей "Hello", заменяются строкой "Howdy" отражением перед выполнением main().

"Привет" и "Howdy" относятся к одному и тому же объекту. Вот почему s.equals("Hello") выводит true System.out.println(a.equals("Howdy")); также выводит true.

Взгляните на точный процесс выполнения: enter image description here