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

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务