String 类
在Java的编程中,字符串是运用的比较多的类型,字符串是由Java的String类来创建的,需要注意的是String类被 final
修饰的类,这意味着该类不能继承,该类的对象值是不可变的。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
但在平时使用的时候,我们经常这样操作来改变String对象变量的值
String str = "hello";
str += "world";
其实这样的操作很消耗内存,浪费存储空间,我们来看下上面的语句块会产生哪些操作。
- 在堆上开辟一个空间用于存储 hello
- 在栈上分配一个空间用于存储str,存储的值是堆内存的 hello 地址
- 在堆上开辟一个空间用于存储 world.
- 拼接 hello 与 world.两个字符串,放入堆另外开辟的一个内存空间
- 栈上存储str的值变为 helloworld. 的堆内存地址
经过上面的步骤分析,我们可以知道,String 类型的字符串在进行拼接的时候,在堆内存上开辟了三次空间,而且只使用其中一个内存空间,这无疑是对堆内存空间的极大的浪费。
为了能解决这个问题,也就引入了 StringBuild 和 StringBuffer。
StringBuffer与StringBuilder
StringBuilder 与 StringBuffer 两个类主要用于对字符串频繁修改的场景,它们是对 String 的优化,在追加字符串的时候不会去堆上开辟新的空间,会直接在已有的堆空间追加。
上图是String、StringBuffer、StringBuilder的层级关系图,我们可以看出StringBuilder与StringBuffer继承了AbstractStringBuilder抽象类,查看源码可知,StringBuilder与StringBuffer最终使用的方法还是它们的父类AbstractStringBuilder中的方法,唯一不同的是StringBuffer比StringBuilder多做了一个加锁的操作,这也就导致StringBuffer是线程安全的,但执行效率比较低,而StringBuilder不是线程安全的,但执行效率较高。
StringBuilder
public StringBuilder(String str) {
super(str.length() + 16);
append(str); // 调用本类的
}
@Override
public StringBuilder append(String str) { // 没有加锁
super.append(str); // 直接调用父类的追加方法
return this;
}
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
StringBuffer
public StringBuffer(String str) {
super(str.length() + 16);
append(str); // 调用本类的
}
@Override
public synchronized StringBuffer append(String str) { // 加锁
toStringCache = null;
super.append(str); // 调用父类的追加方法
return this;
}
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
可能有些疑问,为什么String、StringBuilder、StringBuffer三个类都是被 final 修饰,但只有String的值是不可变的呢?
其实很简单,StringBuilder、StringBuffer确实是被 final 修饰的,而且他们本身也确实是不可变的,但我们需要注意的一点,他们追加的真正操作是在它们的父类中进行的,而它们的父类是没有被 final 修饰,这也就间接的理解StringBuilder、StringBuffer是可变的字符串,追加的时候不需要在堆内存开辟新的空间。
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
总结:三者的区别
- 三者都是被 final 修饰的类,但String类是不可变的,而其它两个可以间接理解为可变的。
- StringBuilder和String不是线程安全的,StringBuffer是线程安全的。
- 效率:StringBuilder > StringBuffer。
- 在字符串变量的值不经常改变的情况下(甚至不变),可以使用String,也可以使用StringBuilder;在字符串变量的值经常改变且不是多线程环境下,使用StringBuilder;在字符串变量的值经常改变且在多线程环境下,使用StringBuffer.