添加String Literal和String Object之间有什么区别?
例如
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); // return false
System.out.println(s4 == s5); // return false
为什么s3
/ s4
未指向与s5
相同的位置?
答案 0 :(得分:3)
因为您正在比较参考文献。要比较内容,请使用s1.equals(s2)
。
如果你的引用比较是故意的,那么你不清楚为什么你期望编译器/ JVM以不同的方式实习或不实习相同的字符串。
答案 1 :(得分:3)
由于s1 + s2
不是常量表达式,因为s1
和s2
不是final
,因此其结果不会被实现,即创建另一个对象来表示它,因此引用比较产生false
。
字符串文字 - 或者更常见的是,作为常量表达式(第15.28节)的值的字符串 - 是“interned”,以便使用String.intern方法共享唯一的实例。
JLS 15.28 Constant Expression:
编译时常量表达式是表示基本类型值的表达式或不突然完成的字符串,仅使用以下内容组成:
- ...
- 引用常量变量的简单名称(§4.12.4)。
JLS 4.12.4定义final
个变量。
如果您将s1
和s2
声明为final
,则s3 == s5
将为true
。
答案 2 :(得分:1)
编辑:我假设您知道您正在比较引用,而不是字符串的内容。如果不是,s3.equals(s5)
就是您要找的(如上所述)。
s3
由编译器优化为"hellohello1"
,也可以重用s4
。 我很惊讶编译器不够智能,不能为。此优化仅允许用于常量表达式(请参阅15.28 of Java Language Specification)。换句话说,对非最终变量的任何赋值都否定了以后优化的可能性。s5
做同样的事情。您使用的是哪个JDK版本?
这是一个简单类的javap -c -l
的输出,它将你的代码包装成一个main方法(不是任何人都要求它,但我很好奇)。那么让我们看看发生了什么:
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;
}
我没有经验阅读字节码,但我会试一试:)
ldc #16
后跟astore_1
表示“加载常量#16并将其存储在插槽1中”。正如您所看到的,这在插槽1 - 4开始时完成了4次,转换为s1,s2,s3和s4(参见LocalVariableTable)。答案 3 :(得分:0)
因为编译器优化了字符串文字的连接。
在实践中,这应该无关紧要(大多数情况下),因为您通常希望使用equals方法比较Strings的相等性,而不是检查对象引用是否相同。
另请注意,您可以使用例如:
实习s5s5 = s5.intern();
但这很少需要。