Java final关键字如何处理字符串不变性?
我有以下代码。我理解java字符串不变性和字符串常量池的概念。我不明白为什么在下面的程序中'name1==name2'结果为false而'name2==name3'结果为true。字符串变量name1、name2和name3如何放置在字符串常量池中Java final关键字如何处理字符串不变性?,java,string,Java,String,我有以下代码。我理解java字符串不变性和字符串常量池的概念。我不明白为什么在下面的程序中'name1==name2'结果为false而'name2==name3'结果为true。字符串变量name1、name2和name3如何放置在字符串常量池中 public class Test { public static void main(String[] args) { final String firstName = "John"; S
public class Test {
public static void main(String[] args) {
final String firstName = "John";
String lastName = "Smith";
String name1 = firstName + lastName;
String name2 = firstName + "Smith";
String name3 = "John" + "Smith";
System.out.println(name1 == name2);
System.out.println(name2 == name3);
}
}
Output:
false
true
让我们看一下带有
final
的字节码:
公共类测试{
公开考试();
代码:
0:aload_0
1:invokespecial#1//方法java/lang/Object。“:()V
4:返回
公共静态void main(java.lang.String[]);
代码:
0:ldc#7//String Smith
2:astore_1
3:aload_1
4:invokedynamic#9,0//invokedynamic#0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
9:astore_2
10:ldc#13//String JohnSmith
12:astore_3
13:ldc#13//String JohnSmith
15:astore 4
17:getstatic#15//fieldjava/lang/System.out:Ljava/io/PrintStream;
20:aload_2
21:aload_3
22:if_acmpne 29
25:iconst_1
26:goto 30
29:iconst_0
30:invokevirtual#21//方法java/io/PrintStream.println:(Z)V
33:getstatic#15//fieldjava/lang/System.out:Ljava/io/PrintStream;
36:aload_3
37:aload 4
39:if_acmpne 46
42:iconst_1
43:转到47
46:iconst_0
47:invokevirtual#21//方法java/io/PrintStream.println:(Z)V
50:返回
}
以及不带final
的字节码:
公共类测试{
公开考试();
代码:
0:aload_0
1:invokespecial#1//方法java/lang/Object。“:()V
4:返回
公共静态void main(java.lang.String[]);
代码:
0:ldc#7//String John
2:astore_1
3:ldc#9//String Smith
5:astore_2
6:aload_1
7:aload_2
8:invokedynamic#11,0//invokedynamic#0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
13:astore_3
14:aload_1
15:invokedynamic#15,0//invokedynamic#1:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
20:astore 4
22:ldc#18//String JohnSmith
24:astore 5
26:getstatic#20//Field java/lang/System.out:Ljava/io/PrintStream;
29:aload_3
30:aload 4
32:if_acmpne 39
35:iconst_1
36:goto 40
39:iconst_0
40:invokevirtual#26//方法java/io/PrintStream.println:(Z)V
43:getstatic#20//fieldjava/lang/System.out:Ljava/io/PrintStream;
46:aload 4
48:aload 5
50:if_acmpne 57
53:iconst_1
54:转到58
57:iconst_0
58:invokevirtual#26//方法java/io/PrintStream.println:(Z)V
61:返回
}
如您所见,使用final
,Java将+
的左侧和右侧都识别为常量,因此它在编译时用常量字符串“JohnSmith”
替换了串联。对makeConcatWithConstants
(连接字符串)的唯一调用是针对firstName+lastName
,因为lastName
不是最终的
在第二个示例中,有两个调用makeConcatWithConstants
,一个调用firstName+lastName
,另一个调用firstName+“Smith”
,因为Java不将firstName
识别为常量
这就是为什么在您的示例中
name1==name2
是false
:name2
是字符串池中的常量“JohnSmith”
,而name1
是在运行时动态计算的。但是,name2
和name3
都是字符串池中的常量,这就是为什么name2==name3
是true
让我们看看带有final
的字节码:
公共类测试{
公开考试();
代码:
0:aload_0
1:invokespecial#1//方法java/lang/Object。“:()V
4:返回
公共静态void main(java.lang.String[]);
代码:
0:ldc#7//String Smith
2:astore_1
3:aload_1
4:invokedynamic#9,0//invokedynamic#0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
9:astore_2
10:ldc#13//String JohnSmith
12:astore_3
13:ldc#13//String JohnSmith
15:astore 4
17:getstatic#15//fieldjava/lang/System.out:Ljava/io/PrintStream;
20:aload_2
21:aload_3
22:if_acmpne 29
25:iconst_1
26:goto 30
29:iconst_0
30:invokevirtual#21//方法java/io/PrintStream.println:(Z)V
33:getstatic#15//fieldjava/lang/System.out:Ljava/io/PrintStream;
36:aload_3
37:aload 4
39:if_acmpne 46
42:iconst_1
43:转到47
46:iconst_0
47:invokevirtual#21//方法java/io/PrintStream.println:(Z)V
50:返回
}
以及不带final
的字节码:
公共类测试{
公开考试();
代码:
0:aload_0
1:invokespecial#1//方法java/lang/Object。“:()V
4:返回
公共静态void main(java.lang.String[]);
代码:
0:ldc#7//Stri
class BatchOConstants {
public static final int HELLO = 5;
}
public class UserOfBatch {
public static void main(String[] args) {
System.out.println(BatchOConstants.HELLO);
}
}
> javac UserOfBatch.java
> java UserOfBatch
5
> javap -c UserOfBatch # javap prints bytecode
public static void main(java.lang.String[]);
Code:
0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
3: iconst_5
4: invokevirtual #15 // Method java/io/PrintStream.println:(I)V
7: return
> rm BatchOConstants.class
> java UserOfBatch
5
public @interface Foo {
long value();
public Main() {
final String a = "hello";
String b = "world";
String c = a + "!";
String d = b + "!";
System.out.println(c == "hello!");
System.out.println(d == "world!");
}
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: ldc #7 // String hello
6: astore_1
start local 1 // java.lang.String a
7: ldc #9 // String world
9: astore_2
start local 2 // java.lang.String b
10: ldc #11 // String hello!
12: astore_3
start local 3 // java.lang.String c
13: aload_2
14: invokedynamic #13, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
19: astore 4
start local 4 // java.lang.String d
Compiled from "Test.java"
public class Test {
public Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String Smith
2: astore_2
3: aload_2
4: invokedynamic #3, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
9: astore_3
10: ldc #4 // String JohnSmith
12: astore 4
14: ldc #4 // String JohnSmith
16: astore 5
18: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
21: aload_3
22: aload 4
24: if_acmpne 31
27: iconst_1
28: goto 32
31: iconst_0
32: invokevirtual #6 // Method java/io/PrintStream.println:(Z)V
35: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
38: aload 4
40: aload 5
42: if_acmpne 49
45: iconst_1
46: goto 50
49: iconst_0
50: invokevirtual #6 // Method java/io/PrintStream.println:(Z)V
53: return
}
4: invokedynamic #3, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
10: ldc #4 // String JohnSmith
14: ldc #4 // String JohnSmith