1. 概述
在Java中处理字符串时,String
和 StringBuffer
是两个重要的类。简单来说,字符串是一系列字符的序列,例如 "java"、"spring" 等。
String
和 StringBuffer
的主要区别在于,String
是不可变的,而 StringBuffer
是可变且线程安全的。
本教程将比较这两个类,并理解它们之间的相似性和差异。
2. String
类
String
类表示字符字符串。Java将所有像 "baeldung" 这样的字符串字面值实现为这个类的一个实例。
创建一个字符串字面量:
String str = "baeldung";
同样,我们也可以创建一个 String
对象:
Char data[] = {‘b’, ‘a’, ‘e’, ‘l’, ‘d’, ‘u’, ‘n’, ‘g’};
String str = new String(data);
我们还可以进行如下操作:
String str = new String(“baeldung”);
字符串是常量且不可变的,这使得它们可以共享。
2.1. 字符串字面量与字符串对象
字符串字面量是不可变的字符串,它们存储在堆内存中的一个特殊区域,称为字符串池。对于具有相同值的字符串字面量,Java不会分配新的内存空间。相反,它使用字符串合并(interning)。
相比之下,JVM会在堆内存中(字符串池之外)为新创建的 String
对象分配单独的内存。
因此,每个字符串对象都有不同的内存地址,尽管它们可能具有相同的值。请注意,字符串字面量仍然是一个 String
对象,但反之则不然。
2.2. 字符串池
字符串字面量存储在Java堆中的预留内存区域,称为字符串池。
2.3. 字符串合并(String Interning)
字符串合并是编译器用来避免重复内存分配的一种优化技术。如果已经有相似的值存在,它会避免为新的字符串字面量分配内存。相反,它会使用已存在的副本:
对 String
的常见操作包括连接、比较和搜索。Java语言还提供了对字符串连接运算符 (+) 和将其他对象转换为字符串的特殊支持。值得注意的是,String
内部使用 StringBuffer
和其 append
方法来执行连接:
String str = "String";
str = str.concat("Buffer");
assertThat(str).isEqualTo("StringBuffer");
assertThat(str.indexOf("Buffer")).isEqualTo(6);
3. StringBuffer
类
StringBuffer
也是一个字符序列,就像 String
一样。然而,与 String
不同,它是可变的。我们可以通过 append()
和 insert()
等方法修改 StringBuffer
。append
方法在 StringBuffer
的末尾添加字符序列,而 insert
方法在指定索引处插入字符序列。StringBuffer
类有重载的方法来处理任何对象,先将对象转换为其字符串表示形式,然后再追加或插入到 StringBuffer
中:
StringBuffer sBuf = new StringBuffer("String");
sBuf.append("Buffer");
assertThat(sBuf).isEqualToIgnoringCase("StringBuffer");
sBuf.insert(0, "String vs ");
assertThat(sBuf).isEqualToIgnoringCase("String vs StringBuffer");
StringBuffer
是线程安全的,可以在多线程环境中工作。同步确保了所有语句的正确执行顺序,避免了数据竞争的情况。
然而,Java 1.5引入了 StringBuilder
,作为在不需要考虑线程安全性的场景下的性能优化替代 StringBuffer
。如果字符串缓冲区被单个线程使用,建议使用 StringBuilder
,因为它在大多数实现下通常更快。
4. 性能比较
String
和 StringBuffer
的性能相似。但是,由于 String
每次修改都需要创建一个新的对象,所有的更改都会针对新创建的 String
进行,这会导致更多的时间和内存消耗。
让我们用 JMH 进行一个快速的微基准测试,比较 String
和 StringBuffer
的连接性能:
@BenchmarkMode(Mode.SingleShotTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Measurement(batchSize = 100000, iterations = 10)
@Warmup(batchSize = 100000, iterations = 10)
@State(Scope.Thread)
public class ComparePerformance {
String strInitial = "springframework";
String strFinal = "";
String replacement = "java-";
@Benchmark
public String benchmarkStringConcatenation() {
strFinal = "";
strFinal += strInitial;
return strFinal;
}
@Benchmark
public StringBuffer benchmarkStringBufferConcatenation() {
StringBuffer stringBuffer = new StringBuffer(strFinal);
stringBuffer.append(strInitial);
return stringBuffer;
}
}
Benchmark Mode Cnt Score Error Units
ComparePerformance.benchmarkStringBufferConcatenation ss 10 16.047 ± 11.757 ms/op
ComparePerformance.benchmarkStringConcatenation ss 10 3.492 ± 1.309 ms/op
5. 比较表
总结一下差异:
String
StringBuffer
String
是一个字符序列,且不可变
StringBuffer
类似于 String
,但可以修改,即它是可变的
由于不可变性,可以轻松共享
只能在同步线程之间共享
每次修改都需要创建一个新的字符串
需要调用特定方法进行修改
修改较慢
修改更快
使用字符串池存储数据
使用堆内存
6. 结论
在这篇文章中,我们比较了 String
和 StringBuffer
类。如往常一样,示例代码可在 GitHub 上找到。