开发者

Difference between addition of String Literals and String objects

What is the difference between an addition of String Literal and String Object?

For example

    String s1 ="hello";
    String s2 ="hello1";
    String s3 ="hello" + "hello1";
    String s4 ="hellohello1";
    String s5 = s1 + s2;

    System.out.println(s3 == s4); // returns true
    System.out.println(s3 == s5); // retu开发者_StackOverflow中文版rn false
    System.out.println(s4 == s5); // return false

Why do s3/s4 not point to the same location as s5?


Because s1 + s2 is not a constant expression, since s1 and s2 are not final, therefore its result is not interned, i.e. another object is created to represent it, so reference comparison produces false.

JLS 3.10.5 String Literals:

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.

JLS 15.28 Constant Expression:

A compile-time constant expression is an expression denoting a value of primitive type or a String that does not complete abruptly and is composed using only the following:

  • ...
  • Simple names that refer to constant variables (§4.12.4).

JLS 4.12.4 defines final variables.

If you declare s1 and s2 as final, s3 == s5 would be true.


Because you're comparing references. To compare contents, use s1.equals(s2).

If your comparison of references is intentional, it is unclear why you're expecting the compiler/JVM to intern or not intern identical strings arising in different ways.


EDIT: I assume that you know that you are comparing references, not the contents of the strings. If no, s3.equals(s5) is what you are looking for (as already mentioned).

s3 is optimized by the compiler to "hellohello1" which is reused for s4 as well. I'm quite surprised that the compiler isn't smart enough to do the same for s5 though. Which JDK version are you using?. This optimization is only allowed for constant expressions (see 15.28 of Java Language Specification). In other words, any assignment to a non-final variable denies the possibility of later optimization.

Here's the output of javap -c -l of a simple class that wraps your code into a main method (not that anybody asked for it, but I'm curious myself). So let's see what's going on:

public static void main(java.lang.String[]);
  Code:
    0:  ldc #16; //String hello
    2:  astore_1
    3:  ldc #18; //String hello1
    5:  astore_2
    6:  ldc #20; //String hellohello1
    8:  astore_3
    9:  ldc #20; //String hellohello1
    11: astore  4
    13: new #22; //class java/lang/StringBuilder
    16: dup
    17: aload_1
    18: invokestatic    #24; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
    21: invokespecial   #30; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
    24: aload_2
    25: invokevirtual   #33; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    28: invokevirtual   #37; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    31: astore  5
    33: getstatic   #41; //Field java/lang/System.out:Ljava/io/PrintStream;
    36: aload_3
    37: aload   4
    39: if_acmpne   46
    42: iconst_1
    43: goto    47
    46: iconst_0
    47: invokevirtual   #47; //Method java/io/PrintStream.println:(Z)V
    50: getstatic   #41; //Field java/lang/System.out:Ljava/io/PrintStream;
    53: aload_3
    54: aload   5
    56: if_acmpne   63
    59: iconst_1
    60: goto    64
    63: iconst_0
    64: invokevirtual   #47; //Method java/io/PrintStream.println:(Z)V
    67: getstatic   #41; //Field java/lang/System.out:Ljava/io/PrintStream;
    70: aload   4
    72: aload   5
    74: if_acmpne   81
    77: iconst_1
    78: goto    82
    81: iconst_0
    82: invokevirtual   #47; //Method java/io/PrintStream.println:(Z)V
    85: return

LocalVariableTable: 
  Start  Length  Slot  Name   Signature
   0     86      0     args   [Ljava/lang/String;
   3     83      1     s1     Ljava/lang/String;
   6     80      2     s2     Ljava/lang/String;
   9     77      3     s3     Ljava/lang/String;
  13     73      4     s4     Ljava/lang/String;
  33     53      5     s5     Ljava/lang/String;


}

I'm not to experienced reading byte code, but I'll give it a go :)

  • numbers starting with # (e.g. #16) are references to the constant pool. The content is always added as a comment to this line
  • ldc #16 followed by astore_1 means "load constant #16 and store it in slot 1". As you can see, this is done 4 times at the beginning for slots 1 - 4 which translates to s1, s2, s3 and s4 (see LocalVariableTable).
  • for s5, without going into much detail, there is obviously a StringBuilder and loading of slot 1 (aload_1) and slot 2 (aload_2) involved before storing the result in slot 5 (astore 5).


Because the compiler optimizes concatenation of String literals.

In practice this should not matter (most of the time), as you usually want to compare Strings for equality using the equals method and not to check if the object references are the same.

Also note that you can intern s5 using e.g.:

s5 = s5.intern();

This is rarely needed, though.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜