如何解决字符串文字和字符串对象的添加之间的区别
|| 字符串文字和字符串对象的添加有什么区别? 例如 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
不在同一位置?
解决方法
由于
s1 + s2
不是常数表达式,由于s1
和s2
不是final
,因此其结果不会被求和,即创建另一个对象来表示它,因此引用比较将生成false
。
JLS 3.10.5字符串文字:
字符串文字(或更一般而言,为常量表达式的值(第15.28节)的字符串)为“ interned \”,以便使用String.intern方法共享唯一的实例。
JLS 15.28常数表达式:
编译时常量表达式是表示原始类型或String的值的表达式,该值不会突然完成,并且仅使用以下内容组成:
...
引用常量变量的简单名称(第4.12.4节)。
JLS 4.12.4定义了“ 7”个变量。
如果将s1
和s2
声明为final
,则s3 == s5
将是true
。
,因为您正在比较参考。要比较内容,请使用s1.equals(s2)
。
如果您对引用的比较是有意的,则不清楚为什么您期望编译器/ JVM内收或不内收以不同方式产生的相同字符串。
,编辑:我假设您知道您正在比较引用,而不是字符串的内容。如果否,那么您正在寻找ѭ16((如上所述)。
编译器将s3
优化为\"hellohello1\"
,ѭ2也重用了s3
。我很惊讶该编译器不够聪明,无法对ѭ3进行相同的处理。您正在使用哪个JDK版本?仅对常量表达式允许这种优化(请参阅Java语言规范的15.28)。换句话说,对非最终变量的任何赋值都拒绝了后续优化的可能性。
这是一个简单类的ѭ21的输出,该类将您的代码包装到一个主要方法中(不是有人要它,但我很好奇我自己)。因此,让我们看看发生了什么:
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;
}
我不是有经验的阅读字节码,但我会努力的:)
以#开头的数字(例如#16)是对常量池的引用。内容总是作为注释添加到此行
ldc #16
后跟astore_1
表示“装入常数#16并将其存储在插槽1中”。如您所见,这在插槽1-4的开头执行了4次,转换为s1,s2,s3和s4(请参阅LocalVariableTable)。
对于s5,无需过多说明,显然有一个StringBuilder并加载了插槽1(in25ѭ)和插槽2(aload_2
),然后将结果存储在插槽5(astore 5
)中。
,因为编译器优化了字符串文字的串联。
实际上,这并不重要(大部分时间),因为您通常希望使用equals方法比较Strings是否相等,而不要检查对象引用是否相同。
另请注意,您可以使用以下内容来实习s5:
s5 = s5.intern();
但是,这很少需要。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。