18.1.2 String、StringBuffer、StringBuilder区别
1. 基本概念对比
1.1 String类特性
- 不可变性:String对象一旦创建,其内容不能被修改
- 线程安全:由于不可变,天然线程安全
- 内存分配:存储在字符串常量池中,支持字符串复用
1.2 StringBuffer类特性
- 可变性:内容可以被修改,不会创建新对象
- 线程安全:方法使用synchronized关键字修饰
- 性能:由于同步开销,性能相对较低
1.3 StringBuilder类特性
- 可变性:内容可以被修改,不会创建新对象
- 线程不安全:没有同步机制,单线程环境下性能最佳
- JDK版本:JDK 1.5引入
2. 底层实现原理
2.1 String底层实现
public final class String implements Serializable, Comparable<String>, CharSequence {
// JDK 8及之前使用char数组
private final char value[];
// JDK 9之后使用byte数组 + 编码标识
private final byte[] value;
private final byte coder;
// 字符串拼接示例
public String concat(String str) {
// 每次拼接都会创建新的String对象
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true); // 创建新对象
}
}
2.2 StringBuffer底层实现
public final class StringBuffer extends AbstractStringBuilder
implements Serializable, CharSequence {
// 继承自AbstractStringBuilder的可变字符数组
char[] value;
int count; // 实际使用的字符数量
// 同步的append方法
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null; // 清除缓存
super.append(str);
return this;
}
// 同步的toString方法
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
}
2.3 StringBuilder底层实现
public final class StringBuilder extends AbstractStringBuilder
implements Serializable, CharSequence {
// 非同步的append方法
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
// 非同步的toString方法
@Override
public String toString() {
return new String(value, 0, count);
}
}
3. 性能对比分析
3.1 字符串拼接性能测试
public class StringPerformanceTest {
private static final int ITERATIONS = 10000;
public static void main(String[] args) {
testStringConcat();
testStringBufferAppend();
testStringBuilderAppend();
}
// String拼接测试
public static void testStringConcat() {
long startTime = System.currentTimeMillis();
String result = "";
for (int i = 0; i < ITERATIONS; i++) {
result += "Hello"; // 每次都创建新对象
}
long endTime = System.currentTimeMillis();
System.out.println("String拼接耗时: " + (endTime - startTime) + "ms");
}
// StringBuffer拼接测试
public static void testStringBufferAppend() {
long startTime = System.currentTimeMillis();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < ITERATIONS; i++) {
sb.append("Hello"); // 在原对象上修改
}
String result = sb.toString();
long endTime = System.currentTimeMillis();
System.out.println("StringBuffer拼接耗时: " + (endTime - startTime) + "ms");
}
// StringBuilder拼接测试
public static void testStringBuilderAppend() {
long startTime = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < ITERATIONS; i++) {
sb.append("Hello"); // 在原对象上修改,无同步开销
}
String result = sb.toString();
long endTime = System.currentTimeMillis();
System.out.println("StringBuilder拼接耗时: " + (endTime - startTime) + "ms");
}
}
// 典型输出结果:
// String拼接耗时: 2156ms
// StringBuffer拼接耗时: 12ms
// StringBuilder拼接耗时: 8ms
3.2 内存使用分析
public class StringMemoryAnalysis {
public static void demonstrateStringMemory() {
// String拼接会产生大量临时对象
String s1 = "Hello";
String s2 = "World";
String s3 = s1 + s2; // 实际上会创建StringBuilder,然后调用toString()
// 等价于:
StringBuilder temp = new StringBuilder();
temp.append(s1);
temp.append(s2);
String s3_equivalent = temp.toString();
// 多次拼接的内存浪费
String result = "";
for (int i = 0; i < 1000; i++) {
result = result + i; // 每次循环创建新String对象
// 会产生1000个临时String对象
}
}
public static void demonstrateBuilderMemory() {
// StringBuilder只创建一个对象,动态扩容
StringBuilder sb = new StringBuilder(16); // 初始容量16
for (int i = 0; i < 1000; i++) {
sb.append(i); // 在同一个对象上操作
}
String result = sb.toString(); // 最后创建一个String对象
}
}
4. 容量管理机制
4.1 StringBuilder容量扩展
public class StringBuilderCapacityDemo {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
System.out.println("初始容量: " + sb.capacity()); // 16
// 添加字符,观察容量变化
sb.append("Hello World!");
System.out.println("添加12个字符后容量: " + sb.capacity()); // 16
sb.append("This is a test");
System.out.println("添加更多字符后容量: " + sb.capacity()); // 34 (16*2+2)
// 手动设置容量
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
Java面试圣经 文章被收录于专栏
Java面试圣经,带你练透java圣经
