小熊奶糖(BearCandy)
小熊奶糖(BearCandy)
发布于 2025-10-11 / 1 阅读
0
0

Java字符串与StringBuilder详解

Java字符串与StringBuilder详解

1. String类的特性

不可变性(Immutability)

public class StringDemo {
    public static void main(String[] args) {
        String str1 = "Hello";
        String str2 = str1 + " World"; // 创建了新对象
    
        System.out.println(str1); // 输出: Hello
        System.out.println(str2); // 输出: Hello World
    
        // 验证不可变性
        String original = "Java";
        String modified = original.concat(" Programming");
    
        System.out.println("Original: " + original); // 输出: Java
        System.out.println("Modified: " + modified); // 输出: Java Programming
    }
}

String拼接的内存问题

public class StringConcatenation {
    public static void main(String[] args) {
        String result = "";
    
        // 每次循环都会创建新的String对象
        for (int i = 0; i < 5; i++) {
            result += "Number " + i + " "; // 低效的拼接方式
            // 等价于: result = new StringBuilder().append(result).append("Number ").append(i).append(" ").toString();
        }
    
        System.out.println(result);
    }
}

2. StringBuilder详解

可变性(Mutability)

public class StringBuilderDemo {
    public static void main(String[] args) {
        // 创建StringBuilder实例
        StringBuilder sb = new StringBuilder();
    
        // 追加内容 - 不会创建新对象
        sb.append("Hello");
        sb.append(" ");
        sb.append("World");
    
        // 转换为String
        String result = sb.toString();
        System.out.println(result); // 输出: Hello World
    
        // 其他常用方法
        sb.insert(5, " Beautiful"); // 在指定位置插入
        System.out.println(sb.toString()); // 输出: Hello Beautiful World
    
        sb.delete(5, 15); // 删除指定范围
        System.out.println(sb.toString()); // 输出: Hello World
    
        sb.reverse(); // 反转字符串
        System.out.println(sb.toString()); // 输出: dlroW olleH
    }
}

3. 性能对比:数组拼接示例

使用String拼接数组(低效)

public class StringArrayConcatenation {
    public static String concatenateWithString(int[] array) {
        String result = "";
        for (int i = 0; i < array.length; i++) {
            result += array[i];
            if (i < array.length - 1) {
                result += ", ";
            }
        }
        return result;
    }
  
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3, 4, 5};
    
        long startTime = System.currentTimeMillis();
        String result = concatenateWithString(numbers);
        long endTime = System.currentTimeMillis();
    
        System.out.println("结果: " + result);
        System.out.println("String拼接耗时: " + (endTime - startTime) + "ms");
    }
}

使用StringBuilder拼接数组(高效)

public class StringBuilderArrayConcatenation {
    public static String concatenateWithStringBuilder(int[] array) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < array.length; i++) {
            sb.append(array[i]);
            if (i < array.length - 1) {
                sb.append(", ");
            }
        }
        return sb.toString();
    }
  
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3, 4, 5};
    
        long startTime = System.currentTimeMillis();
        String result = concatenateWithStringBuilder(numbers);
        long endTime = System.currentTimeMillis();
    
        System.out.println("结果: " + result);
        System.out.println("StringBuilder拼接耗时: " + (endTime - startTime) + "ms");
    }
}

4. 内存占用分析

String拼接的内存消耗过程

public class MemoryAnalysis {
    public static void analyzeStringMemory() {
        String[] words = {"A", "B", "C", "D", "E"};
        String result = "";
    
        System.out.println("String拼接过程的内存对象创建:");
        for (int i = 0; i < words.length; i++) {
            result += words[i];
            System.out.println("第" + (i+1) + "次拼接后: " + result);
            // 每次循环都会创建新的String对象
        }
    }
  
    public static void analyzeStringBuilderMemory() {
        String[] words = {"A", "B", "C", "D", "E"};
        StringBuilder sb = new StringBuilder();
    
        System.out.println("\nStringBuilder拼接过程:");
        for (int i = 0; i < words.length; i++) {
            sb.append(words[i]);
            System.out.println("第" + (i+1) + "次追加后: " + sb.toString());
            // 始终使用同一个StringBuilder对象
        }
    }
  
    public static void main(String[] args) {
        analyzeStringMemory();
        analyzeStringBuilderMemory();
    }
}

5. 为什么StringBuilder更节省内存

详细的内存占用对比

public class DetailedMemoryComparison {
  
    // 使用String拼接 - 产生多个中间对象
    public static void stringConcatenationMemory() {
        int[] array = {1, 2, 3, 4, 5};
        String result = "";
    
        // 内存分析:
        // 循环1: "" + "1" → 创建新String "1"
        // 循环2: "1" + "2" → 创建新String "12"  
        // 循环3: "12" + "3" → 创建新String "123"
        // 循环4: "123" + "4" → 创建新String "1234"
        // 循环5: "1234" + "5" → 创建新String "12345"
        // 总共创建了5个String对象!
    
        for (int num : array) {
            result += num;
        }
    }
  
    // 使用StringBuilder - 只有一个对象
    public static void stringBuilderConcatenationMemory() {
        int[] array = {1, 2, 3, 4, 5};
        StringBuilder sb = new StringBuilder();
    
        // 内存分析:
        // 只创建一个StringBuilder对象
        // 所有append操作都在同一个对象内部进行
        // 最后只创建一个String对象
    
        for (int num : array) {
            sb.append(num);
        }
        String result = sb.toString();
    }
}

6. StringBuilder的内部机制

容量自动扩展

public class StringBuilderInternal {
    public static void showCapacityGrowth() {
        StringBuilder sb = new StringBuilder(); // 初始容量16
    
        System.out.println("初始容量: " + sb.capacity());
    
        for (int i = 0; i < 50; i++) {
            sb.append("x");
            if (i == 15 || i == 34 || i == 49) {
                System.out.println("添加 " + (i+1) + " 个字符后容量: " + sb.capacity());
            }
        }
    }
  
    public static void main(String[] args) {
        showCapacityGrowth();
        /*
        输出示例:
        初始容量: 16
        添加 16 个字符后容量: 34  (16 * 2 + 2)
        添加 35 个字符后容量: 70  (34 * 2 + 2)  
        添加 50 个字符后容量: 70
        */
    }
}

7. 最佳实践建议

选择合适的场景

public class BestPractices {
  
    // 场景1: 简单的字符串连接 - 使用String
    public static String simpleConcatenation() {
        String firstName = "John";
        String lastName = "Doe";
        return firstName + " " + lastName; // 编译器会优化为StringBuilder
    }
  
    // 场景2: 循环中的字符串连接 - 使用StringBuilder
    public static String buildCSV(int[] data) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < data.length; i++) {
            sb.append(data[i]);
            if (i < data.length - 1) {
                sb.append(",");
            }
        }
        return sb.toString();
    }
  
    // 场景3: 预知大致长度 - 设置初始容量
    public static String buildLargeString(String[] words) {
        // 预估最终字符串长度,设置合适的初始容量
        int estimatedLength = words.length * 10; // 假设每个单词平均10字符
        StringBuilder sb = new StringBuilder(estimatedLength);
    
        for (String word : words) {
            sb.append(word).append(" ");
        }
    
        return sb.toString().trim();
    }
  
    // 场景4: 链式调用
    public static String chainedAppend() {
        return new StringBuilder()
            .append("Name: ").append("John")
            .append(", Age: ").append(25)
            .append(", Score: ").append(95.5)
            .toString();
    }
}

总结

StringBuilder节省内存的关键原因:

  1. 对象创建数量

    • String拼接:每次拼接都创建新String对象
    • StringBuilder:只创建一个对象
  2. 内存分配策略

    • String:每次都需要分配新内存
    • StringBuilder:预分配容量,自动扩展,减少内存重新分配
  3. 垃圾回收压力

    • String:产生大量中间对象,增加GC负担
    • StringBuilder:对象数量最少,GC压力小
  4. 性能影响

    • 在循环或大量拼接场景中,StringBuilder的性能优势非常明显

使用建议:

  • 简单拼接:使用 + 操作符
  • 循环或复杂拼接:使用 StringBuilder
  • 多线程环境:使用 StringBuffer
  • 预知长度时:设置合适的初始容量

评论