String,StringBuffer,StringBuilder的区别可能是面试中java基础中最多的问题了。我记得我刚实习那会面试的时候就记得这三个的速度的速度比是StringBuilder>StringBuffer>String,还是死记硬背的,知其然而不知其所以然。今天就写篇博客正式总结下这三个的区别。
首先来看下String的部分源码(后面的代码都是以jdk1.7为准)
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage.字节数组 */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
可以看到String底层是用字节数组实现的,而且用了final修饰,所以说String对象是不可变的。为什么不可变呢,有很多种原因,包括:1. 字符串常量池的需要
2.缓存HashCode
3.线程安全
有兴趣的可以参照这篇文章http://www.programcreek.com/2013/04/why-string-is-immutable-in-java/
在看看StringBuilder,类的继承关系如图
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
StringBuilder类,继承了AbstractStringBuilder抽象类,来看下AbstractStringBuilder的部分源码
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;
/**
* The count is the number of characters used.
*/
int count;
/**
* This no-arg constructor is necessary for serialization of subclasses.
*/
AbstractStringBuilder() {
}
可以看到StringBuilder的底层实现其实也是字节数组char[] ,和String一样。那他们俩有什么不一样呢?
我们来看下关对StringBuilder进行操作的部分源码,
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
public StringBuilder append(String str) {
super.append(str);
return this;
}
// Appends the specified string builder to this sequence.
private StringBuilder append(StringBuilder sb) {
if (sb == null)
return append("null");
int len = sb.length();
int newcount = count + len;
if (newcount > value.length)
expandCapacity(newcount);
sb.getChars(0, len, value, count);
count = newcount;
return this;
}
public StringBuilder append(CharSequence s) {
if (s == null)
s = "null";
if (s instanceof String)
return this.append((String)s);
if (s instanceof StringBuffer)
return this.append((StringBuffer)s);
if (s instanceof StringBuilder)
return this.append((StringBuilder)s);
return this.append(s, 0, s.length());
}
/**
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public StringBuilder append(CharSequence s, int start, int end) {
super.append(s, start, end);
return this;
}
public StringBuilder append(char[] str) {
super.append(str);
return this;
}
可以看到append方法其实都调用父类AbstractStringBuilder的append方法
public AbstractStringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
public AbstractStringBuilder append(StringBuffer sb) {
if (sb == null)
return append("null");
int len = sb.length();
ensureCapacityInternal(count + len);
sb.getChars(0, len, value, count);
count += len;
return this;
}
可以看到这三个重载的方法都是对字节数组的操作。
那String类的操作呢?
对String类进程操作都是通过加号“+”,Think in Java里是这样介绍“+”号的:
可以看到单行String的第一个“+”操作代码会生成StringBuilder对象,然后调用其append()方法。
@Test
public void testCirCle(){
String[] strings={"1","2","3"};
String result = "";
for(int i=0;i<strings.length;i++){
result = result + strings[i];//每次都会生成一个StringBuilder对象
}
System.out.println(result);
}
若在for循环中使用“+”,则每次循环都会生成一个StringBuilder对象。
注意有一种特殊的情况
@Test
public void testPlus(){
String stringPlus="1"+"2"+"3"+"4";
System.out.println(stringPlus);
}
这种情况生成几个StringBuilder?一个?四个?
我们去看下反编译后的这段代码的class源码
可以神奇的看到没有并没有生成一个StringBuilder对象,原来是JVM虚拟机编译代码时在这种全为String的“+”操作情况下进行了优化。
那StringBuffer又有什么不同呢?
首先先看StringBuffer的源码
/*
* @author Arthur van Hoff
* @see java.lang.StringBuilder
* @see java.lang.String
* @since JDK1.0
*/
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
从类的继承关系和源码可以看出StringBuffer和StringBuilder基本一致,主要的区别在于字符串的操作方法。
public synchronized StringBuffer append(Object obj) {
super.append(String.valueOf(obj));
return this;
}
public synchronized StringBuffer append(String str) {
super.append(str);
return this;
}
public synchronized StringBuffer append(StringBuffer sb) {
super.append(sb);
return this;
}
可以StringBuffer的append方法调用的也是父类AbstractStringBuilder的append()方法,只不过其方法用了synchronized修饰,所以其字符串操作是线程安全的。从jdk1.5之后引入的StringBuilder的append()去掉了synchronized关键字,所以StringBuilder的append方法是线程不安全的,但是其节省了同步的资源,所以StringBuilder的append方法要快于StringBuffer的append方法。
总结
String,StringBuffer,StringBuilder的区别
1、string是不可变的,StringBuffer,StringBuilder是可变的,String的每一次操作都是用StringBuilder实现的。
2、String和StringBuffer是线程安全的,StringBuilder是线程不安全的。
3、在字符串的操作速度上,StringBuilder>StringBuffer>String
三者的应用场景
1、字符串定义一次,后面不会对其改变,用String
2、定义字符串后需要对其进行操作,则用StringBuilder,如果有多线程操作字符串的需求则用StringBuffer。
PS:希望这篇博客能帮助一些人。其实这里面还有很多详细的知识点,例如String常量池啊,线程安全的详细描述啊,等等,限于篇幅有限没有过多描述。如果有写的不对的地方,还请大家留言指正和交流讨论。