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圣经