String、StringBuffer 和 StringBuilder 的区别

目录

[1. 概述对比](#1. 概述对比)

[2. String:不可变的字符串](#2. String:不可变的字符串)

[2.1 基本特性](#2.1 基本特性)

[2.2 使用示例](#2.2 使用示例)

[2.3 适用场景](#2.3 适用场景)

[3. StringBuffer:可变的线程安全字符串](#3. StringBuffer:可变的线程安全字符串)

[3.1 基本特性](#3.1 基本特性)

[3.2 使用示例](#3.2 使用示例)

[3.3 适用场景](#3.3 适用场景)

[4. StringBuilder:可变的非线程安全字符串](#4. StringBuilder:可变的非线程安全字符串)

[4.1 基本特性](#4.1 基本特性)

[4.2 使用示例](#4.2 使用示例)

[4.3 适用场景](#4.3 适用场景)

[5. 性能对比](#5. 性能对比)

[5.1 性能测试代码](#5.1 性能测试代码)

[5.2 测试结果](#5.2 测试结果)

[6. 内存使用对比](#6. 内存使用对比)

[6.1 String 的内存使用](#6.1 String 的内存使用)

[6.2 StringBuffer/StringBuilder 的内存使用](#6.2 StringBuffer/StringBuilder 的内存使用)

[7. 最佳实践](#7. 最佳实践)

[7.1 选择原则](#7.1 选择原则)

[7.2 代码示例](#7.2 代码示例)

[7.3 初始化容量优化](#7.3 初始化容量优化)

[8. 总结](#8. 总结)


在 Java 开发中,字符串处理是最常见的操作之一。Java 提供了三种主要的字符串类:String、StringBuffer 和 StringBuilder。理解它们之间的区别对于编写高效、正确的代码至关重要。

1. 概述对比

|----------|-------------|--------------|---------------|
| 特性 | String | StringBuffer | StringBuilder |
| 可变性 | 不可变 | 可变 | 可变 |
| 线程安全 | 是(天生线程安全) | 是(同步方法) | 否(非线程安全) |
| 性能 | 最低(频繁创建新对象) | 中等(有同步开销) | 最高(无同步开销) |
| 使用场景 | 字符串常量、少量操作 | 多线程环境下的字符串操作 | 单线程环境下的字符串操作 |
| 内存效率 | 低(频繁操作时) | 高 | 最高 |
| 版本引入 | Java 1.0 | Java 1.0 | Java 5 |

2. String:不可变的字符串

2.1 基本特性

String 是不可变类,一旦创建就不能修改。任何看似修改 String 的操作实际上都是创建了一个新的 String 对象。

java 复制代码
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
    private final char value[]; // Java 8 及之前
    private final byte[] value; // Java 9 及之后
    // ...
}

2.2 使用示例

java 复制代码
// 创建字符串
String str1 = "Hello"; // 字面量方式,放入字符串常量池
String str2 = new String("Hello"); // 创建新对象

// 字符串操作(都返回新对象)
String result = str1.concat(" World"); // 拼接
String upper = result.toUpperCase();   // 转大写
String sub = result.substring(6);      // 子字符串

System.out.println(str1); // 输出: Hello(原字符串未改变)
System.out.println(result); // 输出: Hello World

2.3 适用场景

  • 字符串常量定义
  • 不需要频繁修改的字符串
  • 作为 HashMap 的键(利用不可变性)
  • 多线程环境下的字符串共享

3. StringBuffer:可变的线程安全字符串

3.1 基本特性

StringBuffer 是可变的字符序列,所有修改方法都使用 synchronized 关键字保证线程安全。

java 复制代码
public final class StringBuffer extends AbstractStringBuilder 
    implements java.io.Serializable, CharSequence {
    
    @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }
    
    // 其他方法也都是同步的
}

3.2 使用示例

java 复制代码
StringBuffer sb = new StringBuffer("Hello");

// 链式操作,直接修改原对象
sb.append(" ")
  .append("World")
  .append("!")
  .insert(5, " Beautiful");

System.out.println(sb.toString()); // 输出: Hello Beautiful World!

// 线程安全示例
StringBuffer threadSafeBuffer = new StringBuffer();
Runnable task = () -> {
    for (int i = 0; i < 1000; i++) {
        threadSafeBuffer.append(i).append(" ");
    }
};

// 多个线程同时操作是安全的
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();

3.3 适用场景

  • 多线程环境下的字符串操作
  • 需要线程安全的字符串构建
  • 复杂的字符串拼接和修改

4. StringBuilder:可变的非线程安全字符串

4.1 基本特性

StringBuilder 与 StringBuffer API 几乎完全相同,但方法没有同步,因此性能更高。

java 复制代码
public final class StringBuilder extends AbstractStringBuilder 
    implements java.io.Serializable, CharSequence {
    
    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
    
    // 方法没有 synchronized 关键字
}

4.2 使用示例

java 复制代码
StringBuilder sb = new StringBuilder("Hello");

// 高效的链式操作
sb.append(" ")
  .append("World")
  .append("!")
  .insert(5, " Amazing");

System.out.println(sb.toString()); // 输出: Hello Amazing World!

// 单线程环境下性能最佳
long startTime = System.currentTimeMillis();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 100000; i++) {
    builder.append(i);
}
long endTime = System.currentTimeMillis();
System.out.println("StringBuilder 耗时: " + (endTime - startTime) + "ms");

4.3 适用场景

  • 单线程环境下的字符串操作
  • 性能要求高的字符串处理
  • 大量的字符串拼接和修改

5. 性能对比

5.1 性能测试代码

java 复制代码
public class StringPerformanceTest {
    private static final int ITERATIONS = 100000;
    
    public static void testString() {
        long start = System.currentTimeMillis();
        String str = "";
        for (int i = 0; i < ITERATIONS; i++) {
            str += i; // 每次循环创建新对象
        }
        long end = System.currentTimeMillis();
        System.out.println("String 耗时: " + (end - start) + "ms");
    }
    
    public static void testStringBuffer() {
        long start = System.currentTimeMillis();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < ITERATIONS; i++) {
            sb.append(i);
        }
        long end = System.currentTimeMillis();
        System.out.println("StringBuffer 耗时: " + (end - start) + "ms");
    }
    
    public static void testStringBuilder() {
        long start = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < ITERATIONS; i++) {
            sb.append(i);
        }
        long end = System.currentTimeMillis();
        System.out.println("StringBuilder 耗时: " + (end - start) + "ms");
    }
    
    public static void main(String[] args) {
        testString();
        testStringBuffer();
        testStringBuilder();
    }
}

5.2 测试结果

java 复制代码
String 耗时: 4235ms
StringBuffer 耗时: 8ms
StringBuilder 耗时: 5ms

6. 内存使用对比

6.1 String 的内存使用

java 复制代码
String result = "";
for (int i = 0; i < 10; i++) {
    result += i; // 创建11个String对象
}
// 产生大量垃圾对象,增加GC压力

6.2 StringBuffer/StringBuilder 的内存使用

java 复制代码
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
    sb.append(i); // 在同一个缓冲区中操作
}
// 只在需要时扩容,内存使用高效

7. 最佳实践

7.1 选择原则

1.使用 String

  • 字符串常量
  • 不需要修改的字符串
  • 多线程共享的字符串

2.使用 StringBuffer

  • 多线程环境下的字符串操作
  • 需要线程安全的字符串构建

3.使用 StringBuilder

  • 单线程环境下的字符串操作
  • 性能要求高的场景
  • 方法内部的字符串处理

7.2 代码示例

java 复制代码
public class StringBestPractices {
    
    // 常量使用 String
    public static final String DATABASE_URL = "jdbc:mysql://localhost:3306/mydb";
    
    // 方法参数使用 String(不可变,安全)
    public void processMessage(String message) {
        // 处理消息
    }
    
    // 单线程字符串构建使用 StringBuilder
    public String buildQuery(List<String> conditions) {
        StringBuilder query = new StringBuilder("SELECT * FROM users WHERE 1=1");
        for (String condition : conditions) {
            query.append(" AND ").append(condition);
        }
        return query.toString();
    }
    
    // 多线程环境使用 StringBuffer
    public class ThreadSafeLogger {
        private StringBuffer logBuffer = new StringBuffer();
        
        public synchronized void log(String message) {
            logBuffer.append(new Date())
                    .append(": ")
                    .append(message)
                    .append("\n");
        }
        
        public String getLog() {
            return logBuffer.toString();
        }
    }
}

7.3 初始化容量优化

java 复制代码
// 如果知道大致长度,可以预先设置容量
StringBuilder sb = new StringBuilder(1000); // 预分配容量
for (int i = 0; i < 1000; i++) {
    sb.append("item").append(i).append(",");
}
// 避免多次扩容,提高性能

8. 总结

|----------|----------|--------------|---------------|
| 方面 | String | StringBuffer | StringBuilder |
| 核心特点 | 不可变 | 可变、线程安全 | 可变、非线程安全 |
| 性能排序 | 最慢 | 中等 | 最快 |
| 线程安全 | 是 | 是 | 否 |
| 内存效率 | 低(频繁操作时) | 高 | 最高 |
| 使用场景 | 常量、少量操作 | 多线程字符串操作 | 单线程字符串操作 |

黄金法则

  • 如果字符串不需要改变:使用 String
  • 如果在单线程中需要改变字符串:使用 StringBuilder
  • 如果在多线程中需要改变字符串:使用 StringBuffer

希望本篇文章对你有帮助。

相关推荐
I'm a winner2 小时前
第五章:Python 数据结构:列表、元组与字典(一)
开发语言·数据结构·python
葵野寺2 小时前
【RelayMQ】基于 Java 实现轻量级消息队列(九)
java·开发语言·rabbitmq·java-rabbitmq
代码不停2 小时前
MySQL联合查询
java·数据库·mysql
nightunderblackcat2 小时前
新手向:C语言、Java、Python 的选择与未来指南
java·c语言·python
纯真时光2 小时前
Maven高级
java
大白同学4213 小时前
【C++】C++11介绍(Ⅱ)
开发语言·c++
你怎么知道我是队长3 小时前
C语言---存储类
c语言·开发语言
XIAOYU6720133 小时前
金融数学专业需要学哪些数学和编程内容?
开发语言·matlab·金融
油炸自行车3 小时前
【Qt】编写Qt自定义Ui控件步骤
开发语言·c++·qt·ui·自定义ui控件·qt4 自定义ui控件