String,StringBuilder 以及 StringBuffer 这三个类的关系与区别一直是 Java 的经典问题,这次就来讲一下关于这三个类的一些知识
一. 简单对比
- String : 字符常量
- StringBuilder : 字符变量
- StringBuffer : 字符变量
String 属于常量类型,被声明为 final class,所有的属性也都是 final 类型,因此 String 对象一旦创建,便不可更改;
StringBuilder / StringBuffer 两个类属于变量类型,是可以更改的,它们都是为了解决字符串由于拼接产生太多中间对象的问题而提供的类。
-
运行速度 StringBuilder > StringBuffer > String
-
线程安全: StringBuffer
-
非线程安全 : StringBuilder
StringBuilder 在本质上和 StringBuffer 没有太大区别,但是由于 StringBuilder 去掉了 StringBuffer 拥有的线程安全部分,因此有效减少了开销。因此,StringBuilder 是大部分情况下字符串拼接操作的首选
二. String 处理字符串
例一:
String s = "abcd";
s = s + "fgh";
很多人作这样的字符串处理的时候会误认为 String 类型是可变的。
但其实 JVM 处理这段代码的过程是这样的:首先创建 s 对象,赋值“abcd” ,然后处理第二行代码时,再创建一个 s 对象,赋值 “abcdfgh”,然后将第一个 s 对象垃圾回收。
所以相当于第一个 s 没更改过,第二个 s 是新的对象
例二:
String str = “This is only a” + “simple” + “test”;
这段代码相当于 String str = “This is only a simple test”;
例三:
String str2 = "This is only a";
String str3 = "simple";
String str4 = "test";
String str1 = str2 +str3 + str4;
这段代码同样会按照例一的过程来处理
三. StringBuilder / StringBuffer 构造特性
这两个对象在构造的过程中,首先按照默认大小申请一个字符数组(char[]), 默认容量为 16 个字符,但如果超出,会使用 Arrays.copyOf() 成倍扩容 16,32,64, 128...,当然这样会影响性能,因此可以在创建对象时按照需要自定义其容量
源代码:
//默认 16 个字符
public StringBuilder() {
super(16);
}
//构造函数定义容量
public StringBuilder(int capacity) {
super(capacity);
}
四. String 与 StringBuilder 处理字符串拼接对比
我们都知道,进行字符串拼接操作时推荐使用 StringBuilder,但是是不是什么时候都推荐使用 StringBuilder 来代替 String 进行字符串拼接?显然不是的。
例一:
String str = "123";
String str1 = str + "456";
String str2 = new StringBuilder().append(str).append("def").toString();
在这种情况下,两种处理方式效率差别不大
在 JDK 8 中, String 的字符串拼接操作会被编译器自动转换为 StringBuilder 并调用 append 方法,由于这样的优化方案,使得两个类在这种情况下的处理效率差别不大;而在 JDK 9 中,为了更加统一字符串操作优化,提供了 StringConcatFactory,作为一个统一的入口,更加优化了字符串拼接操作。
例二:
String str = "";
for (int i = 0; i < 1000; i++) {
str += "12345";
}
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 1000; i++) {
stringBuilder.append("12345");
}
这种情况下,StringBuilder 更快
在循环中,每执行一次 “+”,都会创建一个 String 对象,因此会有大量对象创建和回收的消耗。
简单来说,在循环中对同一个字符串对象做字符串拼接,优先选择 StringBuilder
例三
String str1 = "123" + "456" + "789";
String str2 = new StringBuilder("123").append("456").append("789").toString();
这种情况下,String 更快
我们都知道 String str1 = "123" + "456" + "789";
其实是等同于 String str1 = "123456789";
的,而 StringBuilder 反而需要多次调用 append 方法。