MD. Sahib Bin Mahboob
MD. Sahib Bin Mahboob

Reputation: 20534

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?

Upvotes: 13

Views: 1231

Answers (2)

MD. Sahib Bin Mahboob
MD. Sahib Bin Mahboob

Reputation: 20534

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.

Upvotes: 18

Akhilesh Dhar Dubey
Akhilesh Dhar Dubey

Reputation: 2148

As new String("Hello World") creates new Object in Heap rather than using previously created "Hello World" Object in String Constant Pool.

So, If you write

System.out.print(new String("Hello World").intern());

it will show output as G'Day, Mate. because intern() method return reference id of a string instance from String Constant Pool.

Upvotes: 5

Related Questions