String 和 StringBuilder的真相

不可变String

String 类是被声明为final的, final修饰符说明String类不可被继承,也就是说String的所有方法都将默认是final的. 但是String类不可修改的真正原因并不是因为这个final的类修饰符, 而在于private final char value[]; 中存在字符串的那个字符数组的final. 当final用来修饰成员变量时, 即代表该变量不可修改.因此在对String进行的更改操作都会创建一个全新的String对象, 而不是在原有的对象上做修改. String对象的值存放在字符串常量池.


刚刚看了StringBuilder的源代码,StringBuilder依靠父类AbstractStringBuilder来实现其append操作

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

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;
    }

void expandCapacity(int minimumCapacity) {
	int newCapacity = (value.length + 1) * 2;
        if (newCapacity < 0) {
            newCapacity = Integer.MAX_VALUE;
        } else if (minimumCapacity > newCapacity) {
	    newCapacity = minimumCapacity;
	}
        value = Arrays.copyOf(value, newCapacity);
    }

为何都说StringBuilder比String快呢?

1.String的相加操作会频繁地去创建字符串

比如说String a = "a"; String b = "b";String c = a+b; 则会依次创建"a", "b" ,"ab";

而如果使用了StringBuffer; 依照上面的程序可以看出 首先会申请1+16的空间, 然后在执行append操作时, 系统并没用重新去申请空间, 而是使用了a中余下的16空间. 

这样就避免了频繁的去申请和释放内存空间, 故提升了效率.

2.其实String的相加操作时被默认的转为StringBuilder的append操作的, 这是编译器做的优化,参见字节码

public static void main(String args[]){
		String str = "zlt";
		str = str + "123";
		System.out.println(str);
	}

public static void main (java.lang.String[]){
line0    : ldc  "zlt"
           astore_1  
line3    : new  java.lang.StringBuilder
           dup  
           aload_1  
           invokestatic  java.lang.String java.lang.String.valueOf(java.lang.Object)
           invokespecial  void java.lang.StringBuilder.<init>(java.lang.String)
           ldc  "123"
           invokevirtual  java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String)
        个 invokevirtual  java.lang.String java.lang.StringBuilder.toString()
           astore_1  
           getstatic  java.io.PrintStream java.lang.System.out
           aload_1  
           invokevirtual  void java.io.PrintStream.println(java.lang.String)
line30   : return  

既然最终String的相加操作都被转为了StringBuffer的append操作, 那么为什么说使用StringBuffer的效率会高出很多呢, 这里会频繁的申请StringBuffer变量然后又废弃掉.申请又废弃导致的结果就是:如果你在一个步长为n的for循环中调用了字符串相加操作n次, 那么它将会申请n-1个StringBuffer, 这是何等的奢侈....


至于String 为何声明为final, 在网上的一篇文章看到解释

http://dryr.blog.163.com/blog/static/58211013201071144314720/

1.不允许继承

2.由于String的成员属性也是final的, 这样String类就被设计为一个不变类, 这样有助于共享,提高性能.jvm中具有相同字符串值的字符串常量只有一个.

3.方便多线程对String的访问. java中好多特性依赖于不可变的String类

你可能感兴趣的:(String 和 StringBuilder的真相)