Pytanie Jak działa ten statyczny kod? [duplikować]


To pytanie już zawiera odpowiedź:

Kod:

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) { }
}

Wynik:

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

W jaki sposób ten kod zmienia "Cześć" na "Cześć" podczas drukowania?


14
2017-11-02 05:34


pochodzenie


Używa refleksji, aby zastąpić każdy ciąg, który jest "Cześć" z "Howdy". - The Paramagnetic Croissant
Jest to naprawdę ciekawy! Zauważ, co się stanie, jeśli zrobisz String a  finał. Musi to być jakaś forma optymalizacji kompilatora. - Elliott Frisch
Związane z : 1, 2 - Tiny


Odpowiedzi:


Pierwszy, String literały złożone z tych samych znaków dotyczą tej samej instancji. Więc w

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

obie zmienne odnoszą się do tego samego obiektu.

Druga, static Bloki inicjalizatora są wykonywane, gdy klasa jest najpierw ładowana (i inicjowana). Dzieje się to przed wywołaniem jakichkolwiek metod klasowych, tj. przed main.

Po trzecie, implementacja twojej wersji Java String, prawdopodobnie używa a char\[\] pole do przechowywania ciągu znaków. To pole ma nazwę value. 

Jesteś za pomocą refleksji do odzyskania to char[] dla String obiekt wskazany przez String dosłowny "Howdy".

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

i przypisanie go do char[] dziedzinie String obiekt wskazany przez String dosłowny "Hello"

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

Teraz, kiedy twoja main metoda wykonuje

String a = new String("Hello");

String dosłowny "Hello" odwołuje się do tego samego obiektu, dla którego ustawiłeś char[] pole wcześniej. To char[] zawiera znaki 'H', 'o', 'w', 'd', i 'y' odkąd został wzięty z String obiekt, do którego odnosi się literał "Howdy".

Te znaki są kopiowane do pliku Nowy  String obiekt utworzony tutaj

String a = new String("Hello"); 

9
2017-11-02 06:06





Pierwszą rzeczą, która się dzieje, jest twoja static blok wykonuje. Przez odbicie zmienia wartość String a do "Howdy" (faktycznie to zmienia String  "Hello" do "Howdy" ale ma to ten sam efekt). Jednak dostajesz

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

ponieważ kompilator już zastąpił wartość przez true. Uciekłem javap -v i dostał

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

Tak właśnie się stało. Jak zauważyłem w komentarzach, jeśli się zmienisz String a do

final String a = "Hello";

Ostatnia linia zmienia się na

a --> Hello

z tego samego powodu.


8
2017-11-02 05:58



Nie widzę final zachowanie podczas opisywania go w Javie 7. - Makoto
@Makoto Testowałem z Javą 8. Uwaga, musiałem użyć final String a = "Hello"; nie final String a = new String("Hello"); - Elliott Frisch
Myślę że String literał będzie wykazywał inne zachowanie niż użycie konstruktora. - Boris the Spider


Spójrz tutaj:

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

publiczny zestaw pustych przestrzeni (Object obj, Object value)

Ustawia pole reprezentowane przez ten obiekt Field na określonym   argument obiektu do podanej nowej wartości. Nowa wartość to   automatycznie rozpakowywane, jeśli pole bazowe ma typ pierwotny.

public get (Object obj)

Zwraca wartość pola reprezentowanego przez to pole na   określony obiekt.

Twój blok statyczny jest wykonywany w pierwszej kolejności.

Wszystkie wystąpienia String zawierające "Hello" są zastępowane przez Ciąg "Howdy" przez odbicie przed main() wykonanie.

"Cześć" i "Cześć" odnoszą się do tego samego obiektu. Dlatego s.equals("Hello") wyprowadza true System.out.println(a.equals("Howdy")); będzie również produkować true.

Zapoznaj się z dokładnym procesem wykonania: enter image description here


6
2017-11-02 05:51