StringBuffer、StringBuilder与String剖析

来源:互联网 发布:郑州财经学院网络管 编辑:程序博客网 时间:2024/05/19 09:50

1 StringBuffer与StringBuilder

StringBufferStringBuilder都是final类,不能被继承。


上图是二者的UML图,可以看出,它们都继承了抽象类AbstractBuilderAbstractBuilder定义了StringBufferStringBuilder的基本操作。

这是append方法实现代码:

 public AbstractStringBuilder append(String str) {if (str == null) str = "null";        int len = str.length();if (len == 0) return this;int newCount = count + len;if (newCount > value.length)    expandCapacity(newCount);str.getChars(0, len, value, count);count = newCount;return this;}
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {        if (srcBegin < 0) {            throw new StringIndexOutOfBoundsException(srcBegin);        }        if (srcEnd > count) {            throw new StringIndexOutOfBoundsException(srcEnd);        }        if (srcBegin > srcEnd) {            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);        }        System.arraycopy(value, offset + srcBegin, dst, dstBegin,             srcEnd - srcBegin);    }

这是AbstractStringBuilder 中的append方法,实现原理很简单,AbstractStringBuilder 内部数据结构是字符数组,append方法先判断字符数组长度是否够,如果不够就创建一个更大的字符数组,将原有字符和新字符复制到新建数组中。复制使用的是System类中的arrayCopy方法。

StringBuffer中的append方法:

 public StringBuffer(String str) {super(str.length() + 16);append(str);}

StringBuilder中的append方法:

public StringBuilder append(String str) {super.append(str);        return this;}

从这三个方法可以看出,StringBufferStringBuilder通过继承,直接调用了父类中方法的实现,那这二者有什么区别呢?

StringBufferStringBuilder最大的区别在于:StringBuffer是线程安全的,它的相关方法都加了锁synchronizedStringBuilder是线程不安全的,在单线程环境中,使用StringBuilder比使用StringBuffer效率要高。

2 StringBufferString的效率问题

先上一段测试代码:

class Test {public static void main(String args[]) {String s1 = "s1";String s2 = "s2";String s3 = "s3";String s4 = "s4";String s5 = "s1" + "s2" + "s3" + "s4";String s6 = s1 + s2 + s3 + s4;StringBuffer buf = new StringBuffer();buf.append(s1).append(s2).append(s3).append(s4);}}

下面是使用DJ Java DCompiler3.7反编译Text.class得到的结果:

class Test{Test(){}public static void main(String args[]){ String s1 = "s1"; String s2 = "s2"; String s3 = "s3"; String s4 = "s4"; String s5 = "s1s2s3s4";//对应 String s5="s1"+"s2"+"s3"+"s4" String s6 = (new StringBuilder(String.valueOf(s1))).append(s2).append(s3).append(s4).toString();//对应String s6=s1+s2+s3+s4; StringBuffer buf = new StringBuffer(); buf.append(s1).append(s2).append(s3).append(s4);    }}

从反编译的代码中可以看出:

String s5 = "s1" + "s2" + "s3" + "s4"

经编译器优化后,变为

String s5 = "s1s2s3s4";

故这二者是等价的

String s6 = s1 + s2 + s3 + s4;

反编译之后得到的代码为:

String s6 = (new StringBuilder(String.valueOf(s1))).append(s2).append(s3).append(s4).toString();

已然很明确了,通过“+”进行字符串拼接实际上是调用了StringBuilderappend方法。

这样这两种拼接字符的方法效率就是相同的了。

String str =""; for(int i=0;i<100;i++){   str+=i;  } 
 StringBuffer sb =new  StringBuffer();   for(int i=0;i<100;i++){   sb.append(i);     } 

如果是这种情况,显然是使用StringBuffer效率要高,前者每次使用字符串拼接都会创建一个StringBuilder对象,然后使用toString方法得到拼接后的字符串,显然多做了一些工作。

关于String的特殊性介绍,请看博客String is special点击打开链接

这里补充一些内容:

1、String s = new String("abc");这句代码究竟创建了几个String对象

这段代码调用的是这样的构造方法:

public String(String original) {   //other code …}

所以,String s = new String("abc")可以分解为两步:

1 String original = "abc";

2 String s = new String(original );

这样,显然是创建了两个对象。至于String original = "abc"这句代码的原理,String is special中已经讲得很清楚了。

2、public native String intern();

这是一个本地方法。在调用这个方法时,JAVA虚拟机首先检查字符串池中是否已经存在与该对象值相等对象存在,如果有则返回字符串池中对象的引用;如果没有,则先在字符串池中创建一个相同值的String对象,然后再将它的引用返回。

public class StringInternTest {public static void main(String[] args) {// 使用char数组来初始化a,避免在a被创建之前字符串池中已经存在了值为”abcd”的对象    String a = new String(new char[] { ‘a’, ‘b’, ‘c’, ‘d’ });    String b = a.intern();    if (b == a) {        System.out.println(“b被加入了字符串池中,没有新建对象”);    } else {        System.out.println(“b没被加入字符串池中,新建了对象”);    } }}

运行结果:

b没被加入字符串池中,新建了对象

如果String类的intern()方法在没有找到相同值的对象时,是把当前对象加入字符串池中,然后返回它的引用的话,那么ba指向的就是同一个对象;否则b指向的对象就是JAVA虚拟机在字符串池中新建的,只是它的值与a相同罢了。上面这段代码的运行结果恰恰印证了这一点。

错误之处,请指正,共同学习,谢过!


	
				
		
原创粉丝点击