有这样一个面试题:
String a = "a";
String a1 = new String("a");
String a2 = a1.trim() + "";
String a3 = "a" + "";
String a4 = "a".trim() + "";
System.out.println(a == a1);
System.out.println(a.intern() == a1.intern());
System.out.println(a2 == a1);
System.out.println(a3 == a);
System.out.println(a4 == a);
请问结果是什么?
运行后的结果是:
false
true
false
true
false
为什么结果是这样呢?这需要从String 本身说起。
Java在运行时会维护一个字符串常量池String Pool; 在String a="a"; 首先检查字符串常量池中是否有"a",如果有则直接返回,否则在常量池中创建一个新的;
String a1=new String("a"); 使用new 关键字创建的对象一定在堆栈中,同样也会维护字符串常量池,因为字符串常量池中已经存在了,则不会添加新的。在JAVA中==永远都是比较两个内存地址是否相同,这样因为a和a1不是同一个对象则已定返回false;
intern()方法时返回字符串常量池中的对象,因为常量池中只存在一个“a” 则两者已定相等;
a2.trim()+"" 则是在堆栈中创建了一个新对象,同时维护常量池;则该表达式返回false 但是字符串常量的拼接仅仅维护常量池不会在堆栈中创建新对象则"a"+""还是常量池中的“a”;
为了更直观了解我们可以看一下这段代码的字节码,通过字节码可能更有说服力。
LDC是将常量推送到栈顶,ASTORE 是将栈顶元素赋值给本地变量。
iinvokespecial 调用实例初始化方法,父类实例化方法,私有方法
iinvokevirtual虚方法
通过字节码可以发现 a是常量指向的地址是#2,a1是通过常量“a"new出来的地址是,a2也是new出来的,a3还是常量“a" #2,a4也是new出来的
public class core.demo.StringTest {
public core.demo.StringTest();
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 a
2: astore_1
3: new #3 // class java/lang/String
6: dup
7: ldc #2 // String a
9: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V
12: astore_2
13: new #5 // class java/lang/StringBuilder
16: dup
17: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V
20: aload_2
21: invokevirtual #7 // Method java/lang/String.trim:()Ljava/lang/String;
24: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: ldc #9 // String
29: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
32: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
35: astore_3
36: ldc #2 // String a
38: astore 4
40: new #5 // class java/lang/StringBuilder
43: dup
44: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V
47: ldc #2 // String a
49: invokevirtual #7 // Method java/lang/String.trim:()Ljava/lang/String;
52: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
55: ldc #9 // String
57: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
60: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
63: astore 5
65: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
68: aload_1
69: aload_2
70: if_acmpne 77
73: iconst_1
74: goto 78
77: iconst_0
78: invokevirtual #12 // Method java/io/PrintStream.println:(Z)V
81: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
84: aload_1
85: invokevirtual #13 // Method java/lang/String.intern:()Ljava/lang/String;
88: aload_2
89: invokevirtual #13 // Method java/lang/String.intern:()Ljava/lang/String;
92: if_acmpne 99
95: iconst_1
96: goto 100
99: iconst_0
100: invokevirtual #12 // Method java/io/PrintStream.println:(Z)V
103: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
106: aload_3
107: aload_2
108: if_acmpne 115
111: iconst_1
112: goto 116
115: iconst_0
116: invokevirtual #12 // Method java/io/PrintStream.println:(Z)V
119: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
122: aload 4
124: aload_1
125: if_acmpne 132
128: iconst_1
129: goto 133
132: iconst_0
133: invokevirtual #12 // Method java/io/PrintStream.println:(Z)V
136: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
139: aload 5
141: aload_1
142: if_acmpne 149
145: iconst_1
146: goto 150
149: iconst_0
150: invokevirtual #12 // Method java/io/PrintStream.println:(Z)V
153: return
}