关于字符串的一系列问题
首先我们需要知道String是一个不可变的对象,任何看似会修改其内容的方法,实际上都是创建了一个新的String对象。
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的连接操作,代码如下:
通过以上的分析,我们可以得知,当字符串的操作比较简单时,你可以信赖编译器,使用String,它会为你进行合理的优化。但当如果涉及到循环等复杂操作时,你就应该使用StringBuilder啦。
至于StringBuilder与StringBuffer,两者基本相同,只是StringBuffer的线程安全特性会造成额外的开销,因此StringBuilder的效率要略高于StringBuffer。
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."<init>":()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."<init>":(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."<init>":()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."<init>":(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
}
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."<init>":()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."<init>":(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."<init>":()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."<init>":()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."<init>":(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."<init>":()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
}
通过以上的分析,我们可以得知,当字符串的操作比较简单时,你可以信赖编译器,使用String,它会为你进行合理的优化。但当如果涉及到循环等复杂操作时,你就应该使用StringBuilder啦。
至于StringBuilder与StringBuffer,两者基本相同,只是StringBuffer的线程安全特性会造成额外的开销,因此StringBuilder的效率要略高于StringBuffer。