一、String 、StringBuilder 、StringBuffer 的区别
1、答案
1、String 类型是不可变的(immutable),一旦创建就不能更改它的值。每当对 String 进行修改时,都会创建一个新的 String 对象。。
2、StringBuilder 类型是可变的(mutable),它是非线程安全的。它可以动态地添加、删除或替换字符串中的字符。
3、StringBuffer 类型也是可变的,与 StringBuilder 类型类似,但它在方法上加了synchronized锁,是线程安全的。由于 StringBuffer 对方法进行了同步处理,因此在多线程环境中使用 StringBuffer 可以保证数据的一致性。
2、代码解释
String
public class dome {
public static void main(String[] args) {
// 1. String
String str1 = "Hello";
String str2 = str1.substring(1);
String str3 = str2 + " World";
System.out.println("str1: " + str1);
System.out.println("str2: " + str2);
System.out.println("str3: " + str3);
System.out.println("str1 == str2: " + (str1 == str2));
System.out.println("str1 == str3: " + (str1 == str3));
System.out.println("str2 == str3: " + (str2 == str3));
/*
输入结果:
str2: ello
str3: ello World
str1 == str2: false
str1 == str3: false
str2 == str3: false
这个示例中,我们定义了三个字符串变量 str1、str2 和 str3,并对它们进行一系列操作。
具体来说,我们首先使用 substring() 方法从 str1 中获取子字符串 str2,然后将 str2 和
另一个字符串 " World" 进行拼接,得到新的字符串 str3。
接着,我们输出了三个字符串变量的值,并使用 == 运算符比较它们之间的关系。注意,这里
使用 == 运算符比较的是两个字符串变量的引用地址是否相同,而不是它们所包含的字符序列是否
相同。
因为字符串是不可变的,所以当我们对字符串进行修改时,实际上是创建了新的字符串对象。
从输出结果可以看出,虽然 str1、str2 和 str3 中包含的字符序列有重叠部分,但它们在内存
中是完全独立的对象,它们的引用地址并不相同。这就证明了当我们对字符串进行修改时,Java 确
实会创建新的字符串对象。
*/
}
}
StringBuilder
public class dome {
public static void main(String[] args) throws InterruptedException {
//2.StringBuilder
StringBuilder sb1 = new StringBuilder("Hello");
sb1.append(" World");
System.out.println(sb1.toString()); // "Hello World"
//验证非线程安全
StringBuilder sb2 = new StringBuilder();
//创建线程一
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
sb2.append("1");
}
});
//创建线程2
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
sb2.append("2");
}
});
//开启线程
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(sb2.toString());
/*
在这个案例中,我们创建了一个StringBuilder对象,然后启动了两个线程。每个线程都向
StringBuilder中追加1000个字符,一个是"1",一个是"2"。最后,我们将结果打印到控制台上。
如果StringBuilder是线程安全的,那么我们期望最终结果应该是1000个"1"和1000个"2",并且
它们的顺序可能是交错的。但实际上,由于StringBuilder不是线程安全的,我们很可能得到不同
的结果,在这种情况下,输出的字符串可能是由1000个"1"和一些"2"组成,或者是由1000个"1"和
一些"2"组成,或者是一个混合的字符串。
*/
}
}
StringBuffer
public class dome {
public static void main(String[] args) throws InterruptedException {
//2.StringBuffer
StringBuffer sb2 = new StringBuffer();
//验证线程安全
//创建线程一
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
sb2.append("1");
}
});
//创建线程2
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
sb2.append("2");
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(sb2.toString());
}
}
/*
在这个案例中,我们创建了一个StringBuffer对象,然后启动了两个线程。每个线程都向
StringBuffer中追加1000个字符,一个是"1",一个是"2"。最后,我们将结果打印到控制台上。
如果StringBuffer是线程安全的,那么我们期望最终结果应该是1000个"1"和1000个"2",并且
它们的顺序可能是交错的。但实际上,无论我们跑几次,结果都是我们预期的结果,所以
StringBuffer是线程安全的。
*/
3、总结以及使用
String类:
每当需要更改字符串时,都会创建一个新的String对象,这可能会浪费大量的内存和时间。String适合用于以下情况:
当字符串不需要被修改时;
当需要将字符串作为参数传递给某个方法时,因为String对象是不可变的,所以不必担心在传递过程中被修改。
StringBuilder类:
StringBuilder类也用来存储字符串,但它是可变的。也就是说,可以通过调用方法来修改StringBuilder对象的值,而不必每次都创建一个新的对象。StringBuilder适合用于以下情况:
当需要频繁地修改字符串时;
当需要构建一个字符串时,可以使用append方法将多个字符串拼接在一起。
StringBuffer类:
StringBuffer类和StringBuilder类非常类似,都是可变的字符串,但是StringBuffer类是线程安全的,也就是说,它的方法都是同步的。这使得它适合在多线程环境中使用。StringBuffer适合用于以下情况:
当需要在多个线程中共享可变的字符串时;
当需要频繁地修改字符串时,但又需要线程安全性。
综上所述,如果你需要一个不可变的字符串对象,使用String类;如果你需要频繁修改字符串,或者需要构建一个字符串,使用StringBuilder类;如果你需要在多个线程中共享可变的字符串,或者需要频繁修改字符串并且需要线程安全性,使用StringBuffer类。