StringBuffer 和 StringBuilder
我们之前说过,字符串是不可变的,那么如果我们使用 加号 来进行拼接,那么会在字符串常量池中创建一些我们需要字符串常量,然后在进行拼接,并不是在原来的字符串基础之上进行修改。
String对象一旦被声明就不能被改变,如果进行改变,改变的是对象的应用而已。
而所谓的StringBuffer 和 StringBuilder,其实就是两个类,目的就是为了字符串的修改方便。
StringBuffer 和 StringBuilder 大部分功能是相同的,我们先来主要了解一下StringBuilder。
如果我们使用 加号 来进行拼接,如下例子:
public static void main(String[] args) {
String str = "hello";
String str1 = "world";
str = str + str1;
}
我们来查看这个代码的反汇编:
如上图,在使用 + 拼接之前,编译器先创建了 StringBuilder 对象。
然后再使用了 两次 append()方法来进行拼接。
那么如果我们使用 “ + ” 来进行字符串的拼接,那么java编译器会StringBuilder中的append() 拼接。
那么为什么此处要调用两次append()呢?
其实上述使用 加号 的拼接字符串代码,写成 使用 StringBuilder 来拼接的话,应该是以下写法:
public static void main15(String[] args) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("hello");
stringBuilder.append("world");
}
我们使用StringBuilder类来进行拼接的话,因为这是一个类,那么我们就要创建一个引用类型来引用这个StringBuilder对象。
然后,因为StringBuilder 是一个对象的引用,那么如果我直接用String类的引用类型对接收 StringBuilder的值,是不能接收的:
那么,我们查看反汇编,其中使用的是 toString方法来进行转换:
这样进行赋值:
String str = stringBuilder.toString();
String和StringBuilder和StringBuffer的区别:
- String的拼接会产生大量的临时对象,StringBuffer和StringBuilder不会。
- String拼接会被优化为StringBuilder的append()的拼接。
- 后两者的当中的一些方法,是String中不具备的,比如:逆置方法。
- StringBuffer,StringBuilder 前两者和 String 的最大区别就在于,String的内容无法修改,而StringBuffer,StringBuilder的内容可以修改。频繁修改字符串的情况考虑使用StingBuffer,StringBuilder
逆置reverse()方法:
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("hello");
stringBuilder.reverse();
System.out.println(stringBuilder);//olleh
}
而且因为此处的 reverse()方法是对 StringBuilder 这个对象进行操作的,那么这个方法不需要定义一个String变量去接收 逆置之后的字符串(返回值),直接对 StringBuilder对象中的字符串进行修改。
但是,也可以把结果赋值给另一个 StringBuilder 对象:
StringBuilder stringBuilder1 = stringBuilder.reverse();
System.out.println(stringBuilder1);//olleh
这时候我们去打印这对象中的字符串,发现和之前逆置的字符串是一样的。
但是我们不能去给另一个类型的对象赋值,比如 StringBuilder的逆置字符串结果不能给 StringBuffer对象赋值,同样,反过来也是不行的:
我们发现此时报错了。
String是如何产生大量的临时对象的呢?同样我们来查看反汇编代码:
public static void main(String[] args) {
String str = " ";
for(int i = 0;i < 10;i++)
{
str += 'a';
}
}
首先以会优化,创建了 一个 StringBuilder的对象,然后同样是使用append()方法两次,我们主要来看后面的goto语句:
也就说,在return之前会先goto一下,goto到第5行,那么我们创建 StringBuilder对象是在第11行,那么也就说,在循环结束之前我们会创建很多个StringBuilder对象。
但是我们使用StringBuilder来拼接字符串就不会:
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder();
for(int i = 0;i < 10;i++)
{
stringBuilder.append('a');
}
}
比如这个代码,我们来查看反汇编:
他在第0 行创建了一个对象,然后我们来查看循环的goto语句:
我们发现,它goto的是第10 行代码,那么在每一次循环中他不会再创建对象。
那这个append()拼接,拼接的是一个对象,也就是说,不管我使用多少个append(),只要我前引用的是一个对象,那么拼接都是针对这个对象来进行拼接的,那么上述的代码还可以这样写:
stringBuilder.append("hello").append("world");
他也能实现这个同样的效果,那么他是如何实现这样的效果的呢?我们查看append()方法的源代码:
我们发现StringBuilder中的append()方法的返回值是this,也就是,这个方法返回的是这个类本身,那么作用在对象上,也就说是,这个方法返回的是append的对象本身,例如上述代码,当append("hello")方法调用完之后,返回调用修改之后的对象,那么此时这个代码就变成这个样子了:
StringBuilder和StringBuffer的区别:
我们分别查看StringBuilder和StringBuffer的append()方法的源代码:
不管两个类中append如何实现的,return返回的都是this,但是StringBuffer相对于StringBuilder,多了一个synchronized关键字,
这个synchronized关键字,代表的是 线程安全 ,加了这个关键字,代表这个方法他是 线程安全的。
也就是说,StringBuilder 和 String 一般用于单线程。
而StringBuffer 一般用于多线程。
而导致上面的原因就是因为 synchronized关键字的存在。而我们查看StringBuffer 类的源代码,发现里面的方法都是用 synchronized关键字修饰的:
而在StringBuilder当中就没有这个关键词修饰。
注意:String和StringBuffer类不能直接转换。如果要想互相转换,可以采用如下原则:
- String变为StringBuffer:利用StringBuffer的构造方法或append()方法
- StringBuffer变为String:调用toString()方法