14

Code:

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

Result:

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

How does this code change "Hello" to "Howdy" when printing?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
plue
  • 487
  • 4
  • 6

3 Answers3

9

First, String literals composed of the same characters resolve to the same instance. So in

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

both variables are referring to the same object.

Second, static initializer blocks are executed when a class is first loaded (and initialized). This occurs before any class methods are invoked, ie. before main.

Third, your Java version's implementation of String, presumably, uses a char\[\] field to store the string of characters. This field is named value.

You're using reflection to retrieve this char[] for the String object referenced by the String literal "Howdy".

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

and assigning it to the char[] field of the String object referenced by the String literal "Hello"

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

Now, when your main method executes

String a = new String("Hello");

the String literal "Hello" is referencing the same object for which you set the char[] field previously. This char[] contains the characters 'H', 'o', 'w', 'd', and 'y' since it was taken from the String object referenced by the literal "Howdy".

These characters are copied into a new String object created here

String a = new String("Hello"); 
Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
8

The first thing that happens is your static block executes. By reflection, it changes the value of String a to "Howdy" (actually it changes the String "Hello" to "Howdy" but that has the same effect). However, you get

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

because the compiler has already replaced the value with true. I ran javap -v and got

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

So that is exactly what has happened. As I noted in the comments, if you change String a to

final String a = "Hello";

The last line changes to

a --> Hello

for the same reason.

Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
6

Take a look here:

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

public void set(Object obj, Object value)

Sets the field represented by this Field object on the specified object argument to the specified new value. The new value is automatically unwrapped if the underlying field has a primitive type.

public get(Object obj)

Returns the value of the field represented by this Field, on the specified object.

Your static block is executed in first.

All occurrences of String containing "Hello" are replaced by the String "Howdy" by reflection before the main() execution.

"Hello" and "Howdy" are referring to the same object. This is why s.equals("Hello") outputs true System.out.println(a.equals("Howdy")); would also output true.

Take a look to the exact execution process: enter image description here

Pier-Alexandre Bouchard
  • 5,135
  • 5
  • 37
  • 72