Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to re-reference any final String variable. Please clear me what is happening in given program

Tags:

java

I have written a same program in two different way and both are giving me different output. i am not able to understand why. please correct me. In first program i am getting this output

Original: Umesh

Changed: Xmesh

And in second program i am getting this output.

Original: Umesh

Changed: Umesh

Program-1

import java.lang.reflect.Field;

public class SomeClass {
  public static void main(final String[] args) throws Throwable {
    final String s = "Umesh";
    changeString(s);
  }

  // We need a method so the compiler won't inline "s":
  static void changeString(final String s) throws Throwable {

    System.out.println("Original: " + s);
    final Field field = String.class.getDeclaredField("value");
    field.setAccessible(true);
    final char[] value = (char[]) field.get(s);
    value[0] = 'X';
    System.out.println("Changed: " + s);
  }
}

Program-2

import java.lang.reflect.Field;

public class SomeClass {
  public static void main(final String[] args) throws Throwable {
    final String s = "Umesh";
    System.out.println("Original: " + s);
    final Field field = String.class.getDeclaredField("value");
    field.setAccessible(true);
    final char[] value = (char[]) field.get(s);
    value[0] = 'X';
    System.out.println("Changed: " + s);
  }

}
like image 431
Umesh Avatar asked Aug 27 '15 17:08

Umesh


1 Answers

I'll just start by saying you really, really shouldn't be mucking about with strings like that. :-)

In your first example you aren't "re-referencing" anything (that is, you're not changing what string s refers to), what you're doing is modifying the string it refers to. Even though officially strings are immutable, you're using reflection as a backdoor to modify the undocumented internals of the String implementation in Oracle's JDK (other JDKs may be implemented differently, making that code fail). But the s reference is unchanged. Even with reflection, you can't change the value of a final local variable. (You could change a final field via reflection, but doing so would open you up to the same sort of inconsistencies you're seeing in this example.)

What's happening in your second example is that since s is a final variable you give a literal value to within main, as far as the compiler is concerned, it's a compile-time constant, since String is officially immutable. The compiler is (very) aware of strings and does a fair bit of optimization around them, such as turning "a" + "b" + "c" into simply "abc". So later, when it sees "Original: " + s, it can happily just substitute "Original: Umesh" for that. And again at the end, when it sees "Changed: " + s it can replace that with "Changed: Umesh". It ends up being exactly as though you'd actually written System.out.println("Original: Umesh"); and System.out.println("Changed: Umesh"); in the source code.

The compiler couldn't do that in the first example because s is an argument to the function, rather than a final declared right there in main.

You can see the difference in the bytecode. Compile each of them, then disassemble them via javac -p SomeClass. Here's what I get (I called them Example1 and Example2):

$ javap -c Example1
Compiled from "Example1.java"
public class Example1 {
  public Example1();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."":()V
       4: return

  public static void main(java.lang.String[]) throws java.lang.Throwable;
    Code:
       0: ldc           #2                  // String Umesh
       2: invokestatic  #3                  // Method changeString:(Ljava/lang/String;)V
       5: return

  static void changeString(java.lang.String) throws java.lang.Throwable;
    Code:
       0: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: new           #5                  // class java/lang/StringBuilder
       6: dup
       7: invokespecial #6                  // Method java/lang/StringBuilder."":()V
      10: ldc           #7                  // String Original:
      12: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      15: aload_0
      16: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      22: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      25: ldc           #11                 // class java/lang/String
      27: ldc           #12                 // String value
      29: invokevirtual #13                 // Method java/lang/Class.getDeclaredField:(Ljava/lang/String;)Ljava/lang/reflect/Field;
      32: astore_1
      33: aload_1
      34: iconst_1
      35: invokevirtual #14                 // Method java/lang/reflect/Field.setAccessible:(Z)V
      38: aload_1
      39: aload_0
      40: invokevirtual #15                 // Method java/lang/reflect/Field.get:(Ljava/lang/Object;)Ljava/lang/Object;
      43: checkcast     #16                 // class "[C"
      46: checkcast     #16                 // class "[C"
      49: astore_2
      50: aload_2
      51: iconst_0
      52: bipush        88
      54: castore
      55: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      58: new           #5                  // class java/lang/StringBuilder
      61: dup
      62: invokespecial #6                  // Method java/lang/StringBuilder."":()V
      65: ldc           #17                 // String Changed:
      67: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      70: aload_0
      71: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      74: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      77: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      80: return
}

and

$ javap -c Example2
Compiled from "Example2.java"
public class Example2 {
  public Example2();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."":()V
       4: return

  public static void main(java.lang.String[]) throws java.lang.Throwable;
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                  // String Original: Umesh
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: ldc           #5                  // class java/lang/String
      10: ldc           #6                  // String value
      12: invokevirtual #7                  // Method java/lang/Class.getDeclaredField:(Ljava/lang/String;)Ljava/lang/reflect/Field;
      15: astore_2
      16: aload_2
      17: iconst_1
      18: invokevirtual #8                  // Method java/lang/reflect/Field.setAccessible:(Z)V
      21: aload_2
      22: ldc           #9                  // String Umesh
      24: invokevirtual #10                 // Method java/lang/reflect/Field.get:(Ljava/lang/Object;)Ljava/lang/Object;
      27: checkcast     #11                 // class "[C"
      30: checkcast     #11                 // class "[C"
      33: astore_3
      34: aload_3
      35: iconst_0
      36: bipush        88
      38: castore
      39: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      42: ldc           #12                 // String Changed: Umesh
      44: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      47: return
}

Notice how we don't see any string concatenation (StringBuilder usage) in the second example, the compiler combined the static strings at the compilation stage.

like image 199
T.J. Crowder Avatar answered Sep 28 '22 05:09

T.J. Crowder