String,StringBuffer,StringBuilder全面分析

三者区别是什么?

总的来说:String 是个字符串常量,StringBuilder是一个非线程安全的字符串变量,StringBuffer是一个线程安全的字符串变量。

源码分析:

String是一个不可变的char[] 数组。源码如下:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    private final char value[];

那么我们经常使用的"+"连接两个String,不就得到一个改变的String了吗,这是怎么回事呢?
测试代码如下:

public class StringCode {
    String s1="ni "+"hao "+"ma ?";
    String s2="ni ";
    String s3="hao ";
    String s4="ma ?";
    String s5=s2+s3+s4;
}

反汇编得到代码:

public com.example.demo.code.StringCode();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."":()V
       4: aload_0
       5: ldc           #2                  // String ni hao ma ?
       7: putfield      #3                  // Field s1:Ljava/lang/String;
      10: aload_0
      11: ldc           #4                  // String ni
      13: putfield      #5                  // Field s2:Ljava/lang/String;
      16: aload_0
      17: ldc           #6                  // String hao
      19: putfield      #7                  // Field s3:Ljava/lang/String;
      22: aload_0
      23: ldc           #8                  // String ma ?
      25: putfield      #9                  // Field s4:Ljava/lang/String;
      28: aload_0
      29: new           #10                 // class java/lang/StringBuilder
      32: dup
      33: invokespecial #11                 // Method java/lang/StringBuilder."":()V
      36: aload_0
      37: getfield      #5                  // Field s2:Ljava/lang/String;
      40: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      43: aload_0
      44: getfield      #7                  // Field s3:Ljava/lang/String;
      47: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      50: aload_0
      51: getfield      #9                  // Field s4:Ljava/lang/String;
      54: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      57: invokevirtual #13                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      60: putfield      #14                 // Field s5:Ljava/lang/String;
      63: return
}

我们看到, String s1="ni "+"hao "+"ma ?";这种情况,编译器直接认为是 String s1="ni hao ma ?";
String s5=s2+s3+s4;这种情况,就等同于
String s5=new StringBuilder().append(s1).append(s2).append(s3).toString();
所以前一种情况,不可变字符串内容便是"ni hao ma ?",而后一种是通过StringBuilder.append.toString实现。
我们在看toString源码:

 @Override
  public String toString() {
      // Create a copy, don't share the array
      return new String(value, 0, count);
  }

所以toString其实是new了一个新的对象。
如此,如果在for循环种使用了+去拼接字符串,每次循环都会进行一次new StringBuilder().和new String,产生两个对象。从而增加了系统负担。
所以代码中建议使用StringBuilder或StringBuffer去拼接字符串。

StringBuilder是怎么实现的?

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence{
    .....
    }
abstract class AbstractStringBuilder implements Appendable, CharSequence {
    char[] value;
    int count;
    ......
    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }
    ......
}

StringBuilder继承自AbstractStringBuilder,而在AbstractStringBuilder中定义了一个可变的char[]数组,并实现了数据扩容的方法。从而实现了可变字符串。

StringBuffer是怎么做到线程安全的?

看源码:

public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
	......
	@Override
	public synchronized StringBuffer append(Object obj) {
	    toStringCache = null;
	    super.append(String.valueOf(obj));
	    return this;
	}
	
	@Override
	 public synchronized int capacity() {
	     return value.length;
	 }
	 @Override
	 public synchronized void ensureCapacity(int minimumCapacity) {
	     super.ensureCapacity(minimumCapacity);
	 }
	 @Override
	 public synchronized void trimToSize() {
	     super.trimToSize();
	 }
 }

从源码很明显可以看出,StringBuffer也继承自AbstractStringBuilder,线程安全是通过将方法加synchronized对象锁实现的。

你可能感兴趣的:(源码分析,java)