面试题答案
一键面试性能区别
+
运算符:- 在编译期,Java 编译器会将常量字符串的
+
操作优化为直接拼接。例如String s = "Hello" + "World";
,编译后实际为String s = "HelloWorld";
。 - 但如果其中有变量参与,每次
+
操作都会创建新的String
对象。例如:
- 在编译期,Java 编译器会将常量字符串的
String a = "Hello";
String b = "World";
String result = a + b;
这里会先生成一个StringBuilder
对象(Java 5.0 之后),调用其append
方法拼接字符串,最后调用toString
方法生成新的String
对象。频繁使用+
拼接变量字符串时,性能较差,因为会产生大量临时对象。
2. StringBuilder
:
- 非线程安全,适用于单线程环境。
- 它通过可变的字符数组来存储字符串内容,在拼接字符串时,直接修改内部的字符数组,不会频繁创建新的String
对象。例如:
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append("World");
String result = sb.toString();
- 性能较高,特别是在需要多次拼接字符串的场景下。
3. StringBuffer
:
- 线程安全,内部方法使用synchronized
关键字修饰。
- 同样通过可变字符数组存储字符串,在拼接字符串时也不会频繁创建新的String
对象。例如:
StringBuffer sb = new StringBuffer();
sb.append("Hello");
sb.append("World");
String result = sb.toString();
- 由于线程安全机制的存在,在单线程环境下性能略低于`StringBuilder`,因为同步操作会带来一定的性能开销。
适用场景区别
+
运算符:- 适用于少量字符串常量的拼接,或者简单的一次性拼接操作。例如在
System.out.println("Hello" + " " + "World");
中使用。
- 适用于少量字符串常量的拼接,或者简单的一次性拼接操作。例如在
StringBuilder
:- 适用于单线程环境下,需要频繁拼接字符串的场景,如日志记录、SQL 语句拼接等。例如在构建一个复杂的 SQL 查询语句时:
StringBuilder sql = new StringBuilder("SELECT * FROM users WHERE ");
sql.append("age > 18 AND gender = 'male'");
StringBuffer
:- 适用于多线程环境下,需要拼接字符串的场景。比如多个线程可能同时操作同一个字符串拼接的情况。
高并发场景下的选择
在高并发场景下,应优先选择StringBuffer
进行字符串拼接。因为StringBuilder
是非线程安全的,在多线程环境下可能会出现数据不一致的问题,而StringBuffer
通过synchronized
保证了线程安全,虽然会有一定性能开销,但能确保高并发场景下的正确性。例如在一个多线程的日志记录系统中,不同线程可能同时向日志文件中追加字符串,使用StringBuffer
可以避免数据混乱。
// 高并发场景下示例
class LogAppender implements Runnable {
private static StringBuffer logBuffer = new StringBuffer();
private String message;
public LogAppender(String message) {
this.message = message;
}
@Override
public void run() {
synchronized (logBuffer) {
logBuffer.append(message).append("\n");
}
}
}
这里使用StringBuffer
并通过synchronized
块来确保多线程环境下日志字符串拼接的正确性。