String,StringBuilder,StringBuffer的区别
可变性
首先我们看下String的源码
/** The value is used for character storage. */ private final char value[];
由此可以看出,String类中使用final关键字字符数组来保存字符串,所以是不可变的。
注:在Java中,final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)
1、当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。
2、final修饰的方法不能被重写。
3、当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。
再来看下StringBuilder,StringBuffer的源码
1 public final class StringBuilder 2 extends AbstractStringBuilder 3 implements java.io.Serializable, CharSequence 4 { 5 ... 6 }
1 public final class StringBuffer 2 extends AbstractStringBuilder 3 implements java.io.Serializable, CharSequence 4 { 5 ... 6 }
由源码可以看出,StringBuilder、StringBuffer都继承自AbstractStringBuilder类,我们接下来再看下AbstractStringBuilder类的源码
/** * The value is used for character storage. */ char[] value;
由源码可以看出,StringBuilder、StringBuffer在AbstractStringBuilder中也是使用字符数组保存字符串的,但是这两种都是可变的。
线程安全性
String中的对象是不可变的,也可以理解为常量,线程安全的。
接下来我们继续看下StringBuffer源码,我在这随机截取了源码中的一些方法
1 /** 2 * @throws StringIndexOutOfBoundsException {@inheritDoc} 3 * @since 1.2 4 */ 5 @Override 6 public synchronized String substring(int start, int end) { 7 return super.substring(start, end); 8 } 9 10 /** 11 * @throws StringIndexOutOfBoundsException {@inheritDoc} 12 */ 13 @Override 14 public synchronized StringBuffer insert(int offset, Object obj) { 15 toStringCache = null; 16 super.insert(offset, String.valueOf(obj)); 17 return this; 18 } 19 20 /** 21 * @since 1.4 22 */ 23 @Override 24 public int indexOf(String str) { 25 // Note, synchronization achieved via invocations of other 26 StringBuffer methods 27 return super.indexOf(str); 28 }
由源代码可以看出StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。
再来看下StringBuilder源码中的一些方法
1 @Override 2 public StringBuilder append(Object obj) { 3 return append(String.valueOf(obj)); 4 } 5 6 7 /** 8 * @throws StringIndexOutOfBoundsException {@inheritDoc} 9 */ 10 @Override 11 public StringBuilder insert(int index, char[] str, int offset, 12 int len) 13 { 14 super.insert(index, str, offset, len); 15 return this; 16 } 17 18 19 /** 20 * @throws StringIndexOutOfBoundsException {@inheritDoc} 21 */ 22 @Override 23 public StringBuilder replace(int start, int end, String str) { 24 super.replace(start, end, str); 25 return this; 26 }
由源码可以看出,StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
性能
每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。
StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。
相同情况下使用 StirngBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
1、操作少量的数据 : String
2、单线程操作字符串缓冲区下操作大量数据 : StringBuilder
3、多线程操作字符串缓冲区下操作大量数据 : StringBuffer