Java中字符串实习的奇怪行为
代码如下:
String s = new String("1"); s.intern(); String s2 = "1"; System.out.println(s == s2); String s3 = new String("1")+new String("1"); s3.intern(); String s4 = "11"; System.out.println(s3 == s4);
上面代码的输出是:
false true
我知道s
和s2
是不同的对象,因此结果的计算结果为false,但第二个结果的计算结果为true。 谁能告诉我差异?
这是发生了什么:
例1
String s1 = new String("1"); s1.intern(); String s2 = "1";
- 字符串文字
"1"
(传递给String
构造函数)在地址A处实现 。
字符串s1
在地址B创建,因为它不是文字或常量表达式。 - 对
intern()
的调用无效。 字符串"1"
已经实现,操作结果未分配回s1
。 - 从字符串池中检索值为
"1"
字符串s2
,因此指向地址A.
结果:字符串s1
和s2
指向不同的地址。
例2
String s3 = new String("1") + new String("1"); s3.intern(); String s4 = "11";
- 字符串
s3
在地址C创建。 - 对
intern()
的调用将地址C处的值为"11"
的字符串添加到字符串池中。 - 从字符串池中检索值为
"11"
字符串s4
,因此指向地址C.
结果:字符串s3
和s4
指向相同的地址。
概要
字符串"1"
在调用intern()
被实现,因为它存在于s1 = new String("1")
构造函数调用中。
将构造函数调用更改为s1 = new String(new char[]{'1'})
将使s1 == s2
的比较值为true,因为两者现在都将引用通过调用s1.intern()
显式实现的字符串s1.intern()
。
(我使用了这个答案中的代码来获取有关字符串内存位置的信息。)
对于场景1:
String s = new String("1"); s.intern(); String s2 = "1"; System.out.println(s == s2);
使用字节码 :
0: new #2 // class java/lang/String 3: dup 4: ldc #3 // String 1 6: invokespecial #4 // Method java/lang/String."":(Ljava/lang/String;)V 9: astore_1 10: aload_1 11: invokevirtual #5 // Method java/lang/String.intern:()Ljava/lang/String; 14: pop 15: ldc #3 // String 1
for String s = new String("1");
它将创建一个新的String
对象,它将有一个“1”的新地址,它已经在String Pool中 :
ldc #3 // String 1
对于s2
,作为字节码:
15: ldc #3 // String 1
s2
指向String Pool变量: "1"
,因此s
和s2
具有不同的地址,结果为false
。
对于场景2:
String s3 = new String("1")+new String("1"); s3.intern(); String s4 = "11"; System.out.println(s3 == s4);
使用字节码 :
0: new #2 // class java/lang/StringBuilder 3: dup 4: invokespecial #3 // Method java/lang/StringBuilder."":()V 7: astore_1 8: aload_1 9: ldc #4 // String 1 11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 14: ldc #4 // String 1 16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 22: astore_2 23: aload_2 24: invokevirtual #7 // Method java/lang/String.intern:()Ljava/lang/String; 27: astore_3 28: ldc #8 // String 11
作为字节码 ,您可以看到new String("1")+new String("1");
是使用StringBuilder
创建的
new #2 // class java/lang/StringBuilder
它完全是一个没有String Pool变量的新Object。
在s3.intern()
,此方法将当前s3
添加到Memory String Pool和8: aload_1
。
并且s4
正试图从中加载
ldc #8 // String 11
所以s3
和s4
地址应相等,结果为真。
对于使用groovy的人来说,附加信息是: 行为不同
s.intern()不会更改字符串s。 你应该写:
s = s.intern();