与String interning相关

时间:2014-12-30 07:38:19

标签: java string string-interning

public static void main(String[] args) {

    String a = new String("lo").intern();
    final String d = a.intern();
    String b = "lo";
    final String e = "lo";
    String c = "Hello";
    System.out.println(b==a);//true
    System.out.println(d==a);//true
    System.out.println(e==a);//true
    System.out.println(c=="Hel"+a); //why is this false? when e==a is true
    System.out.println(c=="Hel"+d); //why is this false?
    System.out.println(c=="Hel"+b); //why is this false?
    System.out.println(c=="Hel"+e); //this is true

}

这导致

true
true
true
false
false
false
true

表达式e==a为true意味着相同的引用。那么为什么最后一个表达式是真的但是第四个到最后一个,即c== "Hel"+a是假的?

4 个答案:

答案 0 :(得分:16)

表达式

"Hel" + a

不是编译时间常量。实际上,它编译为:

new StringBuilder().append("Hel").append(a).toString()

(或类似)在运行时创建一个新的String对象。

但是,因为e是最终的,编译器可以确定"Hel"e的值的连接是一个常量值实习生就这样。

答案 1 :(得分:10)

所有这些字符串都是在运行时计算的,这就是它们不同的原因

System.out.println(c=="Hel"+a); //why is this false? when e==a is true
System.out.println(c=="Hel"+d); //why is this false?
System.out.println(c=="Hel"+b); //why is this false?

这个在编译期间计算,因为e是最终的:

System.out.println(c=="Hel"+e); //this is true

如果您将代码更改为:

    System.out.println(c==("Hel"+a).intern()); //why is this false? when e==a is true
    System.out.println(c==("Hel"+d).intern()); //why is this false?
    System.out.println(c==("Hel"+b).intern()); //why is this false?

所有这些都会产生真正的

答案 2 :(得分:5)

Java compilerjavac)会将code in Java翻译为由byte code执行的JVM。 它还为您做了一些优化。您可以使用带有javap参数

-c实用程序检查生成的字节代码

与最终字符串连接

c==a是正确的,因为cfinal 以下是此代码段的字节代码(仅限上次比较):

public static void main(java.lang.String[]);
  Code:
   0:   ldc     #2; //String Hello
   2:   astore_2
   3:   getstatic       #3; //Field java/lang/System.out:Ljava/io/PrintStream;
   6:   aload_2
   7:   ldc     #2; //String Hello
   9:   if_acmpne       16
   12:  iconst_1
   13:  goto    17
   16:  iconst_0
   17:  invokevirtual   #4; //Method java/io/PrintStream.println:(Z)V
   20:  return

}

如您所见,java编译器已将“Hel”与“lo”合并,只是将两个字符串leterals“Hello”进行比较。 Java默认情况下实习字符串文字 - 这就是它返回true的原因

与非最终字符串连接

如果要将字符串文字与非最终字符串变量连接起来,则字节代码将不同:

public static void main(java.lang.String[]);
  Code:
   0:   ldc     #2; //String lo
   2:   astore_1
   3:   ldc     #3; //String Hello
   5:   astore_2
   6:   getstatic       #4; //Field java/lang/System.out:Ljava/io/PrintStream;
   9:   aload_2
   10:  new     #5; //class java/lang/StringBuilder
   13:  dup
   14:  invokespecial   #6; //Method java/lang/StringBuilder."<init>":()V
   17:  ldc     #7; //String Hel
   19:  invokevirtual   #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   22:  aload_1
   23:  invokevirtual   #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   26:  invokevirtual   #9; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   29:  if_acmpne       36
   32:  iconst_1
   33:  goto    37
   36:  iconst_0
   37:  invokevirtual   #10; //Method java/io/PrintStream.println:(Z)V
   40:  return

}

这里我们比较显然返回另一个对象的java/lang/StringBuilder.toString:()Ljava/lang/String;方法的结果 - 它等于“Hello”值而不是引用

您可以在此stackoverflow question

中找到有关比较字符串的详细信息

答案 3 :(得分:1)

即使您使用intern()方法,您仍然需要记住==按引用进行比较而不是值。

所以在

的情况下
System.out.println(c=="Hel"+a);
    System.out.println(c=="Hel"+d); 
    System.out.println(c=="Hel"+b); 

"Hel" + a"Hel" + d"Hel" + b在内存中的新引用不会等于c的引用。

在最后一种情况下,因为字符串值是final,所以评估发生在编译时而不是运行时作为优化,因为它永远不会改变。此外,如果您在考虑定义字符串文字时,Java会在内部实现它们。