一、String、StringBuffer和StringBuilder的源码分析
1.由源码可知String的底层是通过字符数组来实现的,String类的关键源码分析如下:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
//final类型的char数组,所以数据引用变量的值不能变
private final char value[];
//缓存String对象的哈希值
private int hash;
}
String 类中只有两个成员变量一个是value 一个是hash,这个hash是缓存String对象的hash值,value 是一个被final修饰的数组对象,所以只能说value不能再引用到其他对象,但不能说明它所引用的对象的内容不能改变。
但我们看源码就会发现String 类没有给这两个成员变量提供任何的访问方法,所以我们没办法修改所引用对象的内容。因此String 对象一旦被创建,被初始化后的变量也无法被修改了,所以说String 对象是不可变对象。
2.StringBuffer的关键源码分析如下:
public final class StringBuffer extends AbstractStringBuilder
implements java.io.Serializable, CharSequence {
static final long serialVersionUID = 3388685877147921107L;
}
//调用父类的构造函数,创建数组对象。默认为16
public StringBuffer() {
super(16);
}
public StringBuffer(int capacity) {
//按照指定容量创建字符数组
super(capacity);
}
如果在创建对象时构造函数的参数为字符串,则创建的数组的长度为字符长度+16字符的长度,然后再将这个字符串添加到字符数组中,添加的时候会判断原来字符数组中的个数加上这个字符串的长度是否大于这个字符数组的大小,如果大于则进行扩容,如果没有则添加
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
StringBuffer 类继承自AbstractStringBuilder 那再看看AbstractStringBuilder的源码:
abstract class AbstractStringBuilder implements Appendable, CharSequence {
// 这里我们看到,这个数组没有被final修饰,所以引用变量的值可以改变,
//可以引用到其他数组对象
char[] value;
//记录字符的个数
int count;
AbstractStringBuilder() {
}
AbstractStringBuilder(int capacity) {
// 构造函数,创建数组对象
value = new char[capacity];
}
public int length() {
return count;
}
从这些源码我们看到StringBuffer的数组和String的不一样,因为成员变量value数组没有被final修饰所以可以修改它的引用变量的值,即可以引用到新的数组对象。所以StringBuffer对象是可变的
3.StringBuilder类的构造函数与StringBuffer类的构造函数实现方式相同,只是在实现的所有方法上面没有添加synchronize关键字。
(源码省略)
二、String、StringBuffer和StringBuilder的区别(与HashTable(线程安全)和HashMap(线程不安全)的区别类似)
1.运行速度
StringBuilder > StringBuffer > String
2.String <(StringBuffer,StringBuilder)的原因
String:字符串常量,String类是不可变类,任何对String的改变都会引发新的String对象的生成
StringBuffer:字符串变量
StringBuilder:字符串变量
String是“字符串常量”,也就是不可改变的对象。但是比如下面代码:
String s = “abcd”;s = s+1;System.out.print(s);// result : abcd1
JVM是这样解析这段代码的:首先创建对象s,赋予一个abcd,然后再创建一个新的对象s用来执行第二行代码,也就是说我们之前对象s并没有变化,所以我们说String类型是不可改变的对象了,由于这种机制,每当用String操作字符串时,实际上是在不断的创建新的对象,而原来的对象就会变为垃圾被GC回收掉,可想而知这样执行效率会比较低。
而StringBuffer与StringBuilder就不一样了,他们是字符串变量,是可改变的对象,每当我们用它们对字符串做操作时,实际上是在一个对象上操作的.
3.一个特殊的例子:
String str = “This is only a” + “ simple” + “ test”;
StringBuffer builder = new StringBuilder
(“This is only a”).append(“ simple”).append(“ test”);
你会很惊讶的发现,生成str对象的速度简直太快了,而这个时候StringBuffer居然速度上根本一点都不占优势。其实这是JVM的一个把戏,实际上:String str = “This is only a” + “ simple” + “test”;其实就是:String str = “This is only a simple test”;
所以不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的String对象的话,速度就没那么快了,譬如:
String str2 = “This is only a”;
String str3 = “ simple”;
String str4 = “ test”;
String str1 = str2 +str3 + str4;
这时候JVM会规规矩矩的按照原来的方式去做。
4.StringBuilder与 StringBuffer
StringBuilder:线程非安全的
StringBuffer:线程安全的
当我们在字符串缓冲去被多个线程使用是,JVM不能保证StringBuilder的操作是安全的,虽然它的速度最快,但是可以保证StringBuffer是可以正确操作的。当然大多数情况下因为我们是在单线程下进行的操作,所以大多数情况下是建议用StringBuilder而不用StringBuffer的,就是由于速度的原因。
5.对于三者使用的总结:
1)如果要操作少量的数据用 = String
2)单线程操作字符串缓冲区 下操作大量数据 = StringBuilder
3)多线程操作字符串缓冲区 下操作大量数据 = StringBuffer
如有错误,还请指正哦~