上一篇博客讲到String对象一旦被创建该内容就不能被修改了如:

 

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。
        String s = "hello world"; s.substring(6); s.replace("hello","hi"); String s1 = s+"java"; System.out.println(s);//结果:hello world String中的方法只是返回新的字符串,并不改变原来的String对象
        System.out.println(s==s1);//结果:false 字符串拼接也只是创建了一个新的对象而已

 

 

 

  实际上String对象之所以不能被修改其本质因为String对象存储值的成员变量char value[] 无法被修改,如果希望多次修改String或者进行多次字符串拼接特别是在循环体中时,为了防止过多的创建和销毁对象,可以使用到 StringBuffer、StringBuilder;其实是先使用char[],然后可以对这个字符数组进行各种修改操作,最终new一个String对象并且将之前修改好的char[]的值作为String的value。

  StringBuffer、StringBuilder都继承了AbstractStringBuilder抽象类:

abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * The value is used for character storage. */
    char[] value;//没有使用final private修饰,说明该引用可以指向其他char[]对象,同时也是可以被子类访问到的

    /** * The count is the number of characters used. */
    int count;

 

 

 

 

 

 

 接下来看看最常用的append方法,StringBuffer、StringBuilder虽然重写该方法,最终也还是调用的父类append:

 

//AbstractStringBuilder
public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len);//检查value数组是否需要扩容,需要的话进行扩容
        str.getChars(0, len, value, count);//备份String对象中的char[]值到value中
        count += len; return this; } //StringBuilder
@Override public StringBuilder append(String str) { super.append(str); return this; } //StringBuffer
@Override public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; }

 

  StringBuffer、StringBuilder类的代码实现上基本一致,但是StringBuffer中的所有公开的方法有synchronized修饰,说明StringBuffer是可以保证线程安全的,当然StringBuilder没有加锁,效率自然更高,所以在多数情况下不需要考虑线程安全问题时应该使用StringBuilder。

 

再来来看看StringBuffer、StringBuilder的toString方法:

//StringBuilder
@Override public String toString() { // Create a copy, don't share the array 创建副本,不共享数组
        return new String(value, 0, count); }

//StringBuffer @Override public synchronized String toString() { if (toStringCache == null) { toStringCache = Arrays.copyOfRange(value, 0, count); } return new String(toStringCache, true); }

   可以发现StringBuilder、StringBuffer的toString最后创建String对象使用的构造器不同,前者使用的构造器是将StringBuilder的char[] 重新创建一个副本作为String的值;后者是先创建副本,并使用toStringCache变量将副本缓存,然后调用String构造器直接将toStringCache引用的char[]与String共享,不需要在进行数组拷贝,算是一种优化,toStringCache同样是被private修饰并且不可修改,当调用StringBuffer其他的修改char[]方法(如append)时,toStringCache会被重新置为null。

   这里有个问题,为什么StringBuilder toString中char[]不能做缓存并与String共享,应该是因为StringBuffer是同步的,在调用toString()方法的时候,它的value数组不会被修改,而StringBuilder它没有同步,在调用toString()时无法保证成员变量不被修改(如果使用缓存,在创建对象String的同时,有可能该缓存会被其他线程清空),所以需要重新创建一个char[],尽量保证如果其他线程改变了这个char[],不会影响到结果String的生成。

 

 

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄