不可变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类