Как работает этот статический код?
код:
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]()