A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

本帖最后由 上海分校-小影 于 2018-4-10 11:05 编辑

​           很多Java初学者不理解为什么String长度不可变,StringBuilder长度可变的原理,在这里我们一起来看一下.
1.String长度不可变
   ​        String的有一个成员变量char value[],String保存的字符就是保存在这里,下面是String部分构造方法:
[AppleScript] 纯文本查看 复制代码
` /** The value is used for character storage. */
private final char value[];
public String() {
​
    this.value = new char[0];
}
​
public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}
​
public String(char value[]) {
    this.value = Arrays.copyOf(value, value.length);
}`
   其中char value[]是用final修饰的,所以String类是不可变的,即字符串常量,所以每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象。
2. StringBuilder 源码解析
  • StringBuilder继承自AbstractStringBuilder抽象类.成员变量是一个char[],没用final修饰,StringBuilder的操作基本都是围绕这个数组进行:
    `char[] value;//在AbstractStringBuilder中定义          该字符数组在StringBuilder初始默认容量是16        

    [AppleScript] 纯文本查看 复制代码
    Java代码  收藏代码
    //默认构造方法  
    public StringBuilder() {  
            super(16);//默认容量  
    }  
    //指定容量构造方法  
    public StringBuilder(int capacity) {  
            super(capacity);  
    }  
    //指定初始字符串构造方法  
    public StringBuilder (String str) {  
            super(str.length() + 16); //初始容量为字符串长度+16  
            append(str);  
    } ` 


  • 这里通过super调用AbstractStringBuilder的构造方法:

    AbstractStringBuilder(int capacity) {  
            value = new char[capacity];//指定数组容量,初始化字节数组  
    }

成员变量字节数组value初始化完成。
  • append系列重载方法

    • AbstractStringBuilder中的append系列重载方法(StringBuilder中的append方法通过super直接调用该系列方法),是其核心方法,可以处理处理所有的基础类型(比如boolean、int、long、double等)、引用类型的拼接(String、Object、AbstractStringBuilder自身等)
    • 这里以用得最多的append(String)进行讲解:



[AppleScript] 纯文本查看 复制代码
 [mw_shl_code=applescript,true]   public AbstractStringBuilder append(String str) {  
      if (str == null)  
          return appendNull();//注意如果为str为null,会拼接一个"null"字符串  
      int len = str.length();  
      ensureCapacityInternal(count + len);//判断容量是否足够,如不够先扩容,再copy到新扩容后的数组  
      str.getChars(0, len, value, count);//把str copy到 字符数组中  
      count += len;//重新计算字符数组总长度  
      return this;  
  }  

private void ensureCapacityInternal(int minimumCapacity) {  
      // overflow-conscious code  
      if (minimumCapacity - value.length > 0)  
          expandCapacity(minimumCapacity);//容量不够,进行扩容  
  }  
 
  void expandCapacity(int minimumCapacity) {  
      int newCapacity = value.length * 2 + 2;//扩容两倍 + 2  
      if (newCapacity - minimumCapacity < 0)  
          newCapacity = minimumCapacity;  
      if (newCapacity < 0) {//溢出处理  
          if (minimumCapacity < 0) // overflow  
              throw new OutOfMemoryError();  
          newCapacity = Integer.MAX_VALUE;  
      }  
      value = Arrays.copyOf(value, newCapacity); //把老数据复制到扩容后的新数组。  
  }  
[/mw_shl_code]
StringBuilder实际上就是把待拼接的字符串,放到自己的字符数组,如果字符数组的容量不够,需要进行扩容。具体操作是新创建一个字符数组(容量为老数组的2倍+2),再把老数组中的内容copy到新数组。这就是为什么在拼接大量字符串(拼接后超过长度16),最好采用指定容量的方式创建StringBuilder(或StringBuffer),防止拼接过程中不断扩容带来的性能消耗。

2 个回复

倒序浏览
厉害厉害
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马