StringBuffer的特点?
StringBuffer,顾名思义,就是字符串缓冲区,它本质上就是一个用于存储数据的容器。我们知道数组也是一个用于存储数据的容器,那么StringBuffer与数组相比有什么特点呢?
-
StringBuffer的长度是可变的。
-
StringBuffer可以存储不同类型的数据。
-
StringBuffer最终要转成String进行使用。
如何理解,看下面的示例代码:
class Demo {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append(5);
sb.append(true);
sb.append("Java");
sb.append(4.555);
sb.append('A');
System.out.println(sb); // 5trueJava4.555A
}
}
通过上面的打印结果我们可以看出:StringBuffer其实就是一个容器,当我们向容器中存储了很多数据之后,如果我们需要对其中的某个数据进行操作,就需要将这个StringBuffer转换成String。由于StringBuffer的append()返回的也是StringBuffer,所以上面的代码可以写成下面这种形式:
class Demo {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append(5).append(true).append("Java").append(4.555).append('A'); // 方法调用链
System.out.println(sb);
}
}
- 可以对字符串进行修改。
如何理解,看下面的示例代码:
class Demo {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append(5).append(true);
sb.insert(1, 'A');
System.out.println(sb); // 5Atrue
}
}
String类是final类,但是如果我们通过缓冲区的话,就可以对字符串进行修改。
StringBuffer的常用方法?
将这些常用方法分为下面几类:
1.添加:
StringBuffer append(data);
StringBuffer insert(index, data);
2.删除:
StringBuffer delete(start, end);
StringBuffer deleteCharAt(int index);
3.查找:
char charAt(index);
int indexOf(string);
int lastIndexOf(string);
4.修改:
StringBuffer replace(start, end, string);
void setCharAt(index, char);
关于StringBuffer的方法有一些小细节:
-
可以使用delete()方法清空缓冲区:sb.delete(0, sb.length());
-
可以使用reverse()进行字符串的反转。
-
通过查看源码,StringBuffer有以下两个构造方法:
public StringBuffer() {
super(16);
}
public StringBuffer(int capacity) {
super(capacity);
}
我们说过StringBuffer的长度是可变的,那么上面两个构造方法的区别其实在于效率,试想如果我们在执行操作时可以预知字符串的长度大概为30,那么第二种构造方法明显效率就更高,因为第一种构造方法还需要进行扩容操作。
StringBuilder的特点?
查看jdk文档,StringBuilder类是从jdk1.5才出现的,那么为什么会出现这个类呢?考虑下面的代码:
public class Main {
public static void main(String[] args) {
String string = "";
for(int i = 0; i < 10000; i++){
string += "Java"; // 1
}
}
}
上面1处代码的含义是:将原有的string引用变量指向的对象内容与"hello"作字符串相加操作之后得到一个新的字符串,再将这个新的字符串存到另一个新的String对象当中,最后让string引用变量指向这个新生成的对象。也就是说当这个for循环执行完毕后在内存中总共new出了10000个对象,如果这些对象没有及时被回收,会造成很大的内存资源浪费。通过反编译上面代码生成的字节码文件,我们可以得到:
于是我们知道:在执行上面的for循环时,JVM会对“ string += "jAVA"; ”语句进行优化。由于StringBuilder的append()是在原有对象上进行操作,所以最终只new了一个对象,明显耗费的内存资源更小。
同时我们还应该注意到,StringBuilder所具有的功能与StringBuffer所具有的功能一模一样,两者的区别就在于:前者是线程不安全的,而后者是线程安全的(通过查看源码,其很多方法加了synchronized)。也即是由于StringBuilder不需要保证线程同步,所以它的效率一般来讲要比StringBuffer更高。
String、StringBuffer、StringBuilder?
文章的最后,我们结合之前讲过的String(可参考String的那些事)来总结这三者的区别:对String对象的操作实际上是一个不断创建新的对象并且将旧的对象回收的过程,而对StringBuffer和StringBuilder对象的操作只是字符数组的扩容而已。并且String和StringBuffer可以保证线程安全,而StringBuilder无法保证,所以一般来讲三者的执行效率:StringBuilder > StringBuffer > String。
那么三者的应用场景:
-
使用String类的场景:在字符串不经常变化的场景中可以使用String类,例如:常量的声明,少量的变量运算。
-
使用StringBuffer类的场景:在频繁进行字符串运算(如拼接、替换、删除等),并且运行在多线程环境中,则可以考虑使用StringBuffer,例如:XML解析、HTTP参数解析和封装。
-
使用StringBuilder类的场景:在频繁进行字符串运算(如拼接、替换、删除等)。并且运行在单线程环境中,则可以考虑使用StringBuilder类,例如:SQL语句的拼装、JSON封装等。