目录
2、String、StringBuilder 、StringBuffer 的区别
1、StringBuilder 介绍
StringBuilder 对象与 String 对象类似,都是对字符进行操作,只是 StringBuilder 能够被修改。在 StringBuilder 内部,包含有一个可变长的字符序列数组,该数组的长度和内容都可以通过调用方法来进行更改。
// String的数组定义为 final,一旦定义不能再更改
private final char value[]
// StringBuilder的数组定义没有 final,所以可以修改
char[] value;
正常情况下,操作字符串都会使用 String,但是,如果要拼接大量的字符串,使用 StringBuilder 对象进行拼接,性能会更好。
char[] 数组的容量和长度
与 String 类一样,StringBuilder 类也有一个 length() 方法,该方法返回构建器中字符数组的长度。但是,与 String 类不同,虽然每一个 StringBuilder 也有容量,不过调用 capacity() 方法返回的容量总是大于或等于字符串的长度(通常大于),并且该容量会根据需要进行自动扩容。// StringBuilder 可以自动扩容
可以在 StringBuilder 对象上使用 String 的任何方法,首先使用 StringBuilder 的 toString() 方法将 StringBuilder 转换为 String 对象。然后再使用 StringBuilder(string str) 构造函数将 String 对象转回为 StringBuilder 。// StringBuilder 和 String 对象可以相互转换。
例如,使用 StringBuilder 类中的 reverse() 方法来反转字符串:
public class StringBuilderDemo {
public static void main(String[] args) {
String palindrome = "Dot saw I was Tod";
StringBuilder sb = new StringBuilder(palindrome);
sb.reverse(); // reverse it
System.out.println(sb);
}
}
运行这个程序产生的输出:doT saw I was toD,请注意 println() 打印的是一个 StringBuilder ,如下所示:
System.out.println(sb);
在上边这行代码中,sb.toString() 是被隐式调用的。
2、String、StringBuilder 、StringBuffer 的区别
三者共同之处:都是 final 类,都不允许被继承。
三者的不同之处:
StringBuffer 是线程安全的,在多线程中不需要使用额外的同步原语(加锁)。
StringBuilder 是非线程安全的,因为不需要进行加锁操作,速度上会比 StringBuffer 快很多。
String实现了三个接口:Serializable、CarSequence、Comparable<String>,而 StringBuilder 只实现了两个接口 Serializable、CharSequence,所以相比之下 String 的实例可以通过 compareTo() 方法进行比较,其 StringBuilder 和 StringBuffer 不可以。
3、三者在运行速度上的区别
在运行速度方面,其性能对比为:
StringBuilder > StringBuffer > String
String 在字符串拼接方面性能最差,因为 String 为字符串常量,而 StringBuilder 和 StringBuffer 均为字符串变量,String 对象一旦创建,该对象就不能更改,所以拼接字符串就需要不断的创建对象,而后两者的对象是变量,可以在一个对象上进行操作,免去了重复创建对象的过程。// 原因就在于定义的内部数组是否为 final
通过以下代码分析字符串的拼接过程:
public class StringTest {
public static void main(String[] args) {
String str="abc";
System.out.println(str);
str=str+"de";
System.out.println(str);
}
}
// 输出结果:
// abc
// abcde
运行这段代码会发现先输出 “abc”,然后又输出 “abcde”,好像是 str 这个对象被更改了,其实这只是一种假象,JVM 对于这几行代码的处理过程是这样的:首先创建一个 String 对象 str,并把“abc” 赋值给 str,然后在第三行中,JVM 又会创建了一个新的对象,也命名为 str,然后再把原来 str 的值和 “de” 拼接起来再赋值给新的 str,而原来的 str 就会被 JVM 的垃圾回收机制(GC)进行回收。所以,str 实际上并没有被更改。因此,Java 中对 String 对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以它的执行速度最慢。
而 StringBuilder 和 StringBuffer 的对象是变量,对变量进行操作就是直接对该对象进行更改,而不需要进行额外的创建和回收的操作,所以速度会比 String 快得多。// Java 创建对象的过程:类加载检查、内存分配、初始化默认值、初始化方法等
另外,有时候也会这样对字符串进行赋值:
public class StringTest {
public static void main(String[] args) {
String str="abc"+"de";
StringBuilder stringBuilder=new StringBuilder().append("abc").append("de");
System.out.println(str);
System.out.println(stringBuilder.toString());
}
}
// 输出结果:
// abcde
// abcde
这样输出结果也是 “abcde” 和 “abcde”,但是 String 的速度却比 StringBuilder 的反应速度要快很多,这是因为第 1 行中的操作和 String str="abcde";是完全一样的,所以速度会很快,而如果写成下面这种形式:
String str1="abc";
String str2="de";
String str=str1+str2;
那么 JVM 就会像上面说的那样,会不断的创建、回收对象来进行这个操作,所以速度就会很慢。
上述例子的完整代码如下:
public class StringTest {
public static void main(String[] args) {
long a=new Date().getTime();
String cc="";
int n=10000;
for (int i = 0; i < n; i++) {
cc+="."+i;
}
System.out.println("String使用的时间"+(System.currentTimeMillis()-a)/1000.0+"s");
long s1=System.currentTimeMillis();
StringBuilder sb=new StringBuilder();
for (int i = 0; i < n; i++) {
sb.append("."+i);
}
System.out.println("StringBuilder使用的时间"+(System.currentTimeMillis()-s1)/1000.0+"s");
long s2=System.currentTimeMillis();
StringBuffer sbf=new StringBuffer();
for (int i = 0; i < n; i++) {
sbf.append("."+i);
}
System.out.println("StringBuffer使用的时间"+(System.currentTimeMillis()-s2)/1000.0+"s");
}
}
4、线程安全的区别
在线程安全上,StringBuilder 不是线程安全的,而 StringBuffer 是线程安全的
StringBuffer 对象的字符串缓冲区可以被多个线程同时使用,且不存在线程安全问题,原因在于 StringBuffer 中的很多方法都带有 synchronized 关键字。所以如果进行的操作是多线程的,可以使用 StringBuffer,如果在单线程情况下,还是推荐使用速度比较快的 StringBuilder。