关于字符串的一系列问题

首先我们需要知道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的连接操作,代码如下:
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文件进行反编译得到:
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 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();
    }
}
同样进行反编译得到:
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    31
   
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    31
   
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对象,效率的高低显而易见。
通过以上的分析,我们可以得知,当字符串的操作比较简单时,你可以信赖编译器,使用String,它会为你进行合理的优化。但当如果涉及到循环等复杂操作时,你就应该使用StringBuilder啦。

至于StringBuilder与StringBuffer,两者基本相同,只是StringBuffer的线程安全特性会造成额外的开销,因此StringBuilder的效率要略高于StringBuffer。
posted @ 2009-10-14 00:39  诚实小郎君  阅读(335)  评论(1编辑  收藏  举报