String、StringBuffer、StringBuilder的区别

##总结:

  String:底层代码是一个用final修饰的char数组,是一个不可变的字符串,因此是线程安全的。对String字符串进行改变时,都会产生一个新的String对象,然后将指针指向String对象,影响系统性能,适用于少量字符串操作的情况。String初始化时,除了用构造函数进行初始化,还可以直接赋值(可以赋空值)。

  StringBuilder:是可变字符串,支持并发操作,是线程安全的,效率低;适用于多线程下在字符串缓冲区进行大量操作的情况。StringBuilder只能用构造函数的形式来初始化(不可以赋空值)。

    StringBuffer修改字符串的原理:首先创建一个stringbuffer对象,然后调用append方法,最后调用toString方法。使用stringbuffer类时,每次都是对stringbuffer对象本身进行操作,不会生成新对象并改变对象的引用。

  StringBuilder:是可变字符序列,不支持并发操作,是线程不安全的,效率高;适用于单线程下在字符缓冲区进行大量操作的情况。StringBuilder只能用构造函数进行初始化(不可以赋空值)。

  三者的运行速度:StringBuilder>StringBuffer>String

    String为字符串常量,而StringBuffer和StringBuilder均为字符串变量,即String对象一旦创建,该对象就是不可更改的,但后两者的对象是变量,是可以更改的。

    Java中对象String对象进行的操作,实际上是一个不断创建新对象并且将旧对象回收的过程,所以执行速度很慢。在单线程下,StringBuilder效率更快,因为它不需要加锁,不具备多线程安全,而StringBuffer每次都需要判断锁,效率相对较低。

  StringBuilder和StringBuffer都继承于AbstractStringBuilder类,实现了Serializable序列化接口、Appendable接口及CharSequence接口。

一、String

  1.string的底层代码为一个用final修饰的char数组,这就意味着定义一个string字符串后,该字符串的内容是不可变的。

1 public final class String
2     implements java.io.Serializable, Comparable<String>, CharSequence {
3     private final char value[];  //String其实就是用char[]实现的。
4     private int hash; // Default to 0 //缓存字符串的hash Code,默认值为 0
5     //String类的内部就是维护了一个char数组
6     private static final long serialVersionUID = -6849794470754667710L;
7     private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];

  因为string实现了Serializable接口,所以支持序列化(把对象转换为字节序列的过程)和反序列化(把字节序列恢复为对象的过程)。Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。

  2.构造方法

    使用字符数组、字符串构造一个string

    使用字节数组构造一个string

    使用StringBuffer和StringBuilder构造一个string

 1 public String(char value[]) {
 2         this.value = Arrays.copyOf(value, value.length);
 3     }
 4     public String(char value[], int offset, int count) {
 5         if (offset < 0) {
 6             throw new StringIndexOutOfBoundsException(offset);
 7         }
 8         if (count < 0) {
 9             throw new StringIndexOutOfBoundsException(count);
10         }
11         if (offset > value.length - count) {
12             throw new StringIndexOutOfBoundsException(offset + count);
13         }
14         this.value = Arrays.copyOfRange(value, offset, offset+count);
15     }

    这两个构造方法都用到了Arrays工具类的copyOf方法,在这两个方法里面都调用了System.arraycopy方法;因为System.arraycopy是一个系统本地方法,所以这个方法的效率很高,所以在构造string时效率也很高。

  3.compareTo方法(因为string实现了Comparable接口,所以必须实现compareTo方法)

 1 public int compareTo(String anotherString) {
 2         int len1 = value.length;
 3         int len2 = anotherString.value.length;
 4         int lim = Math.min(len1, len2);
 5         char v1[] = value;
 6         char v2[] = anotherString.value;
 7         int k = 0;
 8         while (k < lim) {
 9             char c1 = v1[k];
10             char c2 = v2[k];
11             if (c1 != c2) {
12                 return c1 - c2;
13             }
14             k++;
15         }
16         return len1 - len2;
17     }

  4.length,charAt方法

 1 public int length() {
 2         return value.length;
 3     }
 4     public boolean isEmpty() {
 5         return value.length == 0;
 6     }
 7     public char charAt(int index) {
 8         if ((index < 0) || (index >= value.length)) {
 9             throw new StringIndexOutOfBoundsException(index);
10         }
11         return value[index];
12     }

  5.getBytes方法

    调用了StringCoding.encode方法,encode方法调用另外一个重载的方法。

 1 static byte[] encode(char[] ca, int off, int len) {
 2         String csn = Charset.defaultCharset().name();
 3         try {
 4             return encode(csn, ca, off, len);
 5         } catch (UnsupportedEncodingException x) {
 6             warnUnsupportedCharset(csn);
 7         }
 8         try {
 9             return encode("ISO-8859-1", ca, off, len);
10         } catch (UnsupportedEncodingException x) {
11             MessageUtils.err("ISO-8859-1 charset not available: "+ x.toString());
12             System.exit(1);
13             return null;
14         }
15     }

二、StringBuffer

  1.StringBuffer继承于AbstracctStringBuilder抽象类,实现了Serializable接口和CharSequence接口。

1 public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence
2 static final long serialVersionUID = 3388685877147921107L;
3 public StringBuffer() {
4         super(16);
5     }

  2.StringBuffer初始化及扩容机制

    1)StringBuffer()的初始容量为16,当该对象的实体存放的字符长度大于16时,实体容量就会自动增加。StringBuffer对象可以通过length()方法获取实体中存放的字符序列长度,通过capacity()方法来获取当前实体的实际容量。

    2)StringBuffer(int size)可以指定分配给该对象的实体的初始容量参数为参数size指定的字符个数。当该对象的实体存放的字符长度大于size个字符时,实体的容量就会自动增加,以便存放所增加的字符。

    3)StringBuffer(String s)指定给对象的实体的初始容量为参数字符串s的长度额外再加16个字符。当该对象的实体存放的字符序列长度大于size个字符时,实体的容量自动增加。

 1 public StringBuffer(String str) {
 2 //这个执行父类的带参构造函数AbstractStringBuilder(int capacity) 
 3         super(str.length() + 16);
 4         append(str);
 5     }
 6 //append 是用 synchronized 修饰的,所以是线程安全的
 7 public synchronized StringBuffer append(String str) {  
 8         //执行父类的append(str)  
 9         super.append(str);  
10         return this;  
11 } //父类 AbstractStringBuilder 的 append 方法

  扩容算法:使用append方法在字符串后面追加内容时,如果长度超过了子符串存储空间大小就需要进行扩容:创建新的存储空间更大的字符串,将旧的复制过去;再进行字符串append添加时,会先计算添加后字符串的大小,传入一个方法:ensureCapacityInternal这个方法进行是否扩容的判断,需要扩容就调用expandCapacity方法进行扩容,尝试将新容量扩大为原来大小的2倍+2,if判断一下,容量如果还不够,直接扩充到需要的容量大小。

  添加字符串过程:

    -->先检查内部char[]数组是否需要扩容;

    -->如果需要扩容则进行扩容,然后将原来的元数据copy到新数组中;

    -->再将新添加的元数据加入到新数组中。

 1 public AbstractStringBuilder append(String str) {
 2         if (str == null)
 3             return appendNull();
 4         int len = str.length();
 5         //检查char[]数组是否需要扩容,扩容,并将原来的数据copy进去新扩容的数组中
 6         ensureCapacityInternal(count + len);
 7         //将新添加的数据添加到StringBuilder中的char[]数组中,实现字符串的添加
 8         str.getChars(0, len, value, count);
 9         count += len;
10         return this;
11     }
12 /**
13 *元数组char[]的扩容过程
14 */
15     void expandCapacity(int minimumCapacity) {
16         int newCapacity = value.length * 2 + 2;
17         if (newCapacity - minimumCapacity < 0)
18             newCapacity = minimumCapacity;
19         if (newCapacity < 0) {
20             if (minimumCapacity < 0) // overflow
21                 throw new OutOfMemoryError();
22             newCapacity = Integer.MAX_VALUE;
23         }
24         value = Arrays.copyOf(value, newCapacity);
25     }
26 /**
27 *扩容实现
28 */
29    public static char[] copyOf(char[] original, int newLength) {
30         char[] copy = new char[newLength];
31         System.arraycopy(original, 0, copy, 0,
32                          Math.min(original.length, newLength));
33         return copy;
34     }
35   if (str == null)
36             return appendNull();
37         int len = str.length();
38         //检查char[]数组是否需要扩容,扩容,并将原来的数据copy进去新扩容的数组中
39         ensureCapacityInternal(count + len);
40         //将新添加的数据添加到StringBuilder中的char[]数组中,实现字符串的添加
41         str.getChars(0, len, value, count);
42         count += len;
43         return this;
44 }

三、StringBuilder

    

   

 

posted @ 2019-10-23 22:44  MrHH  阅读(256)  评论(0编辑  收藏  举报