Search code examples
javareflectionpuzzle

Java Puzzle with reflection and String


This source outputs G'Day Mate. How this is happening ?

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

And if we change main functions "Hello World" to new String("Hello World"):

System.out.println(new String("Hello World"));

It outputs Hello world .

What is happening actually?


Solution

  • This source code opens up some interesting techniques of java. Let's examine one by one.

    At first we need to understand the flow of the code. Which part of the code will execute first?

    The Static Initialization Block. Why? Let's consult with Java Language Specification (12.4) :

    Initialization of a class consists of executing its static initializers and the initializers for static fields (class variables) declared in the class.

    And when does it occur? Again from JLS (12.4.1):

    T is a class and a static method declared by T is invoked.

    So we can come to the conclusion that static initiazlier will execute first before the main method.

    Now, these two lines are using reflection:

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

    We can break the fist line into two lines for simplicity:

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

    The first line is retrieving the Reflected Class Object and the second line is retrieving a Field which represents the value field of the String class.

    value.setAccessible(true) indicates that the reflected class object should suppress Java language access checking when it is used.(Reference).

    Next line under question is

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

    If we dive into .set() documenation we can see that we are calling the set(Object aObject,Object value) version of set. value.get("G'Day Mate.") is returning "G'Day Mate."'s value field's value which is actually a char[]. And with the call of set it replaces the value of "Hello World" object's value field with "G'Day Mate." object's value field.

    The static block's code is explained.

    Lets dive into main funciton. It's pretty simple. It should output Hello, world. But it is outputting G'Day Mate. Why? Because the Hello, world String object we created in the static initializer is the same as Hello, world object we are using in main function. Consulting with JLS again will shed light on it

    Moreover, a string literal always refers to the same instance of class String. This is because string literals - or, more generally, strings that are the values of constant expressions (§15.28) - are "interned" so as to share unique instances, using the method String.intern.

    This answer can help you to understand the fact more concisely.

    So it is showing different value as we have already changed Hello,world object's value to G'Day, Mate.

    But if you use new String("Hello world") in main function it will directly create a fresh instance of String rather than checking into its pool. So Hello world of main function would be differnt than Hello world of static initializer of which we have changed the value.