String、StringBuffer、StringBuilder

也说String。

  • String:不可变字符序列。
  • StringBuffer:线程安全的可变字符序列。
  • StringBuilder:StringBuffer的非线程安全实现,JDK1.5+。
public final class String { 
         private final char value[];   
         public String(String original) { 
              // 把原字符串original切分成字符数组并赋给value[]; 
         } 
}

//StringBuffer  
public final class StringBuffer extends AbstractStringBuilder  { 
         char value[]; //继承了父类AbstractStringBuilder中的value[] 
         public StringBuffer(String str) { 
                 super(str.length() + 16); //继承父类的构造器,并创建一个大小为str.length()+16的value[]数组 
                 append(str); //将str切分成字符序列并加入到value[]中 
        } 
}

 

1    常量池

在Java源代码中的每一个字面值字符串,都会在编译成class文件阶段,形成标志号为8(CONSTANT_String_info)的常量表。 当JVM加载 class文件的时候,会为对应的常量池建立一个内存数据结构(StringTable,它是一个hashtable,key是字符串的hashcode,value是字符串的引用地址。),并存放在方法区中。同时JVM会自动为CONSTANT_String_info常量表中的字符串常量字面值在堆中创建新的String对象(intern字符串对象)。然后把CONSTANT_String_info常量表的入口地址转变成这个堆中String对象的直接地址(常量池解析)。

示例:

String sc="ab"+"cd"; 
String sd="abcd"; 
System.out.println(sc==sd); //true,在应用启动完成时就已经加载至常量池了 

 

2    StringBuilder与String+

String sa = "a";
String s = sa + "b";

javap -c查看虚拟机指令:

       0: ldc           #2                  // String a
       2: astore_1
       3: new           #3                  // class java/lang/StringBuilder
       6: dup
       7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      10: aload_1
      11: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      14: ldc           #6                  // String b
      16: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      22: astore_2
      23: return

 

由上可以看到,String+实际上的操作就是new StringBuilder().append().append….toString()。因此如下方法不会引起效率问题(除非链接字符串较长时,因为String+操作默认初始化大小16导致append时多次resize):

String method(String sa) {
     return sa + "b";
}

 

一般所说的String+效率低下的主要产生在如下情况:

String method(String sa) {
      for (int i=0; i<100; i++) {
            sa += "b";
      }
      return sa;
}

 

每做一次+就产生一个StringBuilder对象,然后append后丢掉。下次循环重新产生一个StringBuilder对象。如果我们直接使用StringBuilder对象全程append的话,就可以节省N-1次创建与销毁对象的时间。

通过上面的说明,我们可以知道如下结果:

String sa = "ab";  
String sb = "cd";  
String sab = sa+sb; 
String s = "abcd"; 
System.out.println(sab==s); // false 

 

3    String.intern

String.intern可以将一个String类的保存到一个全局StringTable表中,如果具有相同值的Unicode字符串已经在这个表中,那么该方法返回表中已有字符串的地址,如果在表中没有相同值的字符串,则将自己的地址注册到表中。

通过这个方法我们可以测试常量池到底占用JVM的哪一区域。

 

posted @ 2016-07-24 00:09  源梦  阅读(410)  评论(0编辑  收藏  举报