String的不可变性会为程序带来效率问题,因此JVM会将新创建的String对象放入一个常量池中,待下次用到相同String对象时,编译器会从常量池中取得,而不是重新new一个String对象。
看下面的面试题:
String s = new String("abcdefg");这一语句创建了几个String对象?
分析:当程序运行时,会首先创建一个字符串对象"abcdefg",并将其放入常量池中。当执行new操作时,又会在堆中创建一个String对象,该对象是对常量池中"abcdefg"串的一个拷贝。而String s仅仅是声明了一个指向字符串类型的引用,并没有创建任何字符串对象。
因此,答案是2个。
另外,关于String,StringBuilder和StringBuffer三者之间的区别也是面试中经常遇到的问题。
String与StringBuilder的区别主要体现在做字符串连接操作的效率上。
先看String的连接操作,代码如下:
public
class
StringConnection {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String str = " my " ;
String introduction = str + " name " + " is " + " cgw. " ;
System.out.println(introduction);
}
}
再使用javap命令将class文件进行反编译得到:
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String str = " my " ;
String introduction = str + " name " + " is " + " cgw. " ;
System.out.println(introduction);
}
}
Compiled from
"
StringConnection.java
"
public class StringConnection extends java.lang.Object{
public StringConnection();
Code:
0 : aload_0
1 : invokespecial # 8 ; // Method java/lang/Object."":()V
4 : return
public static void main(java.lang.String[]);
Code:
0 : ldc # 16 ; // String my
2 : astore_1
3 : new # 18 ; // class java/lang/StringBuilder
6 : dup
7 : aload_1
8 : invokestatic # 20 ; // Method java/lang/String.valueOf:(Ljava/lang/Objec
t;)Ljava / lang / String;
11 : invokespecial # 26 ; // Method java/lang/StringBuilder."":(Ljava/la
ng / String;)V
14 : ldc # 29 ; // String name
16 : invokevirtual # 31 ; // Method java/lang/StringBuilder.append:(Ljava/lang
/ String;)Ljava / lang / StringBuilder;
19 : ldc # 35 ; // String is
21 : invokevirtual # 31 ; // Method java/lang/StringBuilder.append:(Ljava/lang
/ String;)Ljava / lang / StringBuilder;
24 : ldc # 37 ; // String cgw.
26 : invokevirtual # 31 ; // Method java/lang/StringBuilder.append:(Ljava/lang
/ String;)Ljava / lang / StringBuilder;
29 : invokevirtual # 39 ; // Method java/lang/StringBuilder.toString:()Ljava/l
ang / String;
32 : astore_2
33 : getstatic # 43 ; // Field java/lang/System.out:Ljava/io/PrintStream;
36 : aload_2
37 : invokevirtual # 49 ; // Method java/io/PrintStream.println:(Ljava/lang/St
ring;)V
40 : return
}
我们可以看到,在main方法内的第3行,JVM为我们new了一个StringBuilder对象,然后调用append方法进行字符串的连接,既然编译器能为我们自动选择性能较优的StringBuilder,那我们为什么不能随意使用String呢?再看看下面的代码:
public class StringConnection extends java.lang.Object{
public StringConnection();
Code:
0 : aload_0
1 : invokespecial # 8 ; // Method java/lang/Object."
4 : return
public static void main(java.lang.String[]);
Code:
0 : ldc # 16 ; // String my
2 : astore_1
3 : new # 18 ; // class java/lang/StringBuilder
6 : dup
7 : aload_1
8 : invokestatic # 20 ; // Method java/lang/String.valueOf:(Ljava/lang/Objec
t;)Ljava / lang / String;
11 : invokespecial # 26 ; // Method java/lang/StringBuilder."
ng / String;)V
14 : ldc # 29 ; // String name
16 : invokevirtual # 31 ; // Method java/lang/StringBuilder.append:(Ljava/lang
/ String;)Ljava / lang / StringBuilder;
19 : ldc # 35 ; // String is
21 : invokevirtual # 31 ; // Method java/lang/StringBuilder.append:(Ljava/lang
/ String;)Ljava / lang / StringBuilder;
24 : ldc # 37 ; // String cgw.
26 : invokevirtual # 31 ; // Method java/lang/StringBuilder.append:(Ljava/lang
/ String;)Ljava / lang / StringBuilder;
29 : invokevirtual # 39 ; // Method java/lang/StringBuilder.toString:()Ljava/l
ang / String;
32 : astore_2
33 : getstatic # 43 ; // Field java/lang/System.out:Ljava/io/PrintStream;
36 : aload_2
37 : invokevirtual # 49 ; // Method java/io/PrintStream.println:(Ljava/lang/St
ring;)V
40 : return
}
public
class
StringBuilderConnection {
/**
* 使用String进行字符串连接
* @param fields
* @return
*/
public String StringConnection(String[] fields) {
String result = "" ;
for ( int i = 0 ; i < fields.length; i ++ ) {
result += fields[i];
}
return result;
}
/**
* 使用StringBuilder进行字符串连接
* @param fields
* @return
*/
public String SBConnection(String[] fields) {
StringBuilder result = new StringBuilder();
for ( int i = 0 ; i < fields.length; i ++ ) {
result.append(fields[i]);
}
return result.toString();
}
}
同样进行反编译得到:
/**
* 使用String进行字符串连接
* @param fields
* @return
*/
public String StringConnection(String[] fields) {
String result = "" ;
for ( int i = 0 ; i < fields.length; i ++ ) {
result += fields[i];
}
return result;
}
/**
* 使用StringBuilder进行字符串连接
* @param fields
* @return
*/
public String SBConnection(String[] fields) {
StringBuilder result = new StringBuilder();
for ( int i = 0 ; i < fields.length; i ++ ) {
result.append(fields[i]);
}
return result.toString();
}
}
Compiled from
"
StringBuilderConnection.java
"
public class StringBuilderConnection extends java.lang.Object{
public StringBuilderConnection();
Code:
0 : aload_0
1 : invokespecial # 8 ; // Method java/lang/Object."":()V
4 : return
public java.lang.String StringConnection(java.lang.String[]);
Code:
0 : ldc # 16 ; // String
2 : astore_2
3 : iconst_0
4 : istore_3
5 : goto 32
8 : new # 18 ; // class java/lang/StringBuilder
11 : dup
12 : aload_2
13 : invokestatic # 20 ; // Method java/lang/String.valueOf:(Ljava/lang/Objec
t;)Ljava / lang / String;
16 : invokespecial # 26 ; // Method java/lang/StringBuilder."":(Ljava/la
ng / String;)V
19 : aload_1
20 : iload_3
21 : aaload
22 : invokevirtual # 29 ; // Method java/lang/StringBuilder.append:(Ljava/lang
/ String;)Ljava / lang / StringBuilder;
25 : invokevirtual # 33 ; // Method java/lang/StringBuilder.toString:()Ljava/l
ang / String;
28 : astore_2
29 : iinc 3 , 1
32 : iload_3
33 : aload_1
34 : arraylength
35 : if_icmplt 8
38 : aload_2
39 : areturn
public java.lang.String SBConnection(java.lang.String[]);
Code:
0 : new # 18 ; // class java/lang/StringBuilder
3 : dup
4 : invokespecial # 45 ; // Method java/lang/StringBuilder."":()V
7 : astore_2
8 : iconst_0
9 : istore_3
10 : goto 24
13 : aload_2
14 : aload_1
15 : iload_3
16 : aaload
17 : invokevirtual # 29 ; // Method java/lang/StringBuilder.append:(Ljava/lang
/ String;)Ljava / lang / StringBuilder;
20 : pop
21 : iinc 3 , 1
24 : iload_3
25 : aload_1
26 : arraylength
27 : if_icmplt 13
30 : aload_2
31 : invokevirtual # 33 ; // Method java/lang/StringBuilder.toString:()Ljava/l
ang / String;
34 : areturn
}
我们可以看到,在StringConnection方法中,第5行和第35行构成了一个循环,而StringBuilder的创建操作是在循环体的内部进行的,因此该方法会导致创建多个StringBuilder对象。而在SBConnection方法中,StringBuilder的创建操作是在循环体外,至始至终都只会创建一个StringBuilder对象,效率的高低显而易见。
public class StringBuilderConnection extends java.lang.Object{
public StringBuilderConnection();
Code:
0 : aload_0
1 : invokespecial # 8 ; // Method java/lang/Object."
4 : return
public java.lang.String StringConnection(java.lang.String[]);
Code:
0 : ldc # 16 ; // String
2 : astore_2
3 : iconst_0
4 : istore_3
5 : goto 32
8 : new # 18 ; // class java/lang/StringBuilder
11 : dup
12 : aload_2
13 : invokestatic # 20 ; // Method java/lang/String.valueOf:(Ljava/lang/Objec
t;)Ljava / lang / String;
16 : invokespecial # 26 ; // Method java/lang/StringBuilder."
ng / String;)V
19 : aload_1
20 : iload_3
21 : aaload
22 : invokevirtual # 29 ; // Method java/lang/StringBuilder.append:(Ljava/lang
/ String;)Ljava / lang / StringBuilder;
25 : invokevirtual # 33 ; // Method java/lang/StringBuilder.toString:()Ljava/l
ang / String;
28 : astore_2
29 : iinc 3 , 1
32 : iload_3
33 : aload_1
34 : arraylength
35 : if_icmplt 8
38 : aload_2
39 : areturn
public java.lang.String SBConnection(java.lang.String[]);
Code:
0 : new # 18 ; // class java/lang/StringBuilder
3 : dup
4 : invokespecial # 45 ; // Method java/lang/StringBuilder."
7 : astore_2
8 : iconst_0
9 : istore_3
10 : goto 24
13 : aload_2
14 : aload_1
15 : iload_3
16 : aaload
17 : invokevirtual # 29 ; // Method java/lang/StringBuilder.append:(Ljava/lang
/ String;)Ljava / lang / StringBuilder;
20 : pop
21 : iinc 3 , 1
24 : iload_3
25 : aload_1
26 : arraylength
27 : if_icmplt 13
30 : aload_2
31 : invokevirtual # 33 ; // Method java/lang/StringBuilder.toString:()Ljava/l
ang / String;
34 : areturn
}
通过以上的分析,我们可以得知,当字符串的操作比较简单时,你可以信赖编译器,使用String,它会为你进行合理的优化。但当如果涉及到循环等复杂操作时,你就应该使用StringBuilder啦。
至于StringBuilder与StringBuffer,两者基本相同,只是StringBuffer的线程安全特性会造成额外的开销,因此StringBuilder的效率要略高于StringBuffer。