我们知道String是最常用的一个字符串类,但是问到String、StringBuffer、StringBuilder 三者的区别时,往往会着重区别StringBuffer与StringBuilder的区别,因为三者都是final修饰的常量类,而String字符串的定义类中@Stable private final byte[] value;
从这可以看出,String字符串对象一旦创建后值就不可更改,也就是每次操作字符串的时候,其实都是new的一个新的字符串进行的操作,可想而知在有大量操作的场景下效率是极低的,因此才会引申出StringBuffer与StringBuilder类的操作(对象值可修改)。
那问到StringBuffer与StringBuilder的区别时,张口就来StringBuffer是线程安全的,因为StringBuffer相关的方法都是加了synchronized关键字(同步锁),而StringBuilder是线程不安全的。此处还有一点,看过源码的伙子应该知道StringBuffer是从JDK1.0就开始用了,而StringBuilder是从JDK1.5才开始引入的,所以好好想想既然有了StringBuffer,为什么还要从JDK1.5引入StringBuilder呢?最值的研究的当然就是两者在单线程下的区别了!
java中用于处理字符串常用的有三个类:
java.lang.String
java.lang.StringBuffer
java.lang.StrungBuilder
三者共同之处:都是final类,不允许被继承,主要是从性能
和安全性
上考虑的,因为这几个类都是经常被使用,且考虑到防止其中的参数被修改影响到其他线程的应用。
@Stable private final byte[] value;
在修改时不会改变自身;若修改,等于重新生成新的字符串对象。char[] value;
,没有final修饰,可知这两种对象都是可变的,在修改时会改变对象自身,每次操作都是对对象本身进行修改,不是生成新的对象;使用场景:对字符串经常改变情况下,主要方法:append(),insert()等。@Override
@HotSpotIntrinsicCandidate
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
@Override
public synchronized StringBuffer insert(int offset, String str) {
toStringCache = null;
super.insert(offset, str);
return this;
}
旧容量*2 + 2
; @Override
public synchronized StringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}
@Override
@HotSpotIntrinsicCandidate
public synchronized String toString() {
if (toStringCache == null) {
return toStringCache =
isLatin1() ? StringLatin1.newString(value, 0, count)
: StringUTF16.newString(value, 0, count);
}
return new String(toStringCache);
}
这三个类之间的最主要区别是在两个方面,即运行速度和线程安全这两方面。
首先说运行速度,或者说是执行速度,在这方面运行速度快慢为:
StringBuilder > StringBuffer > String
String最慢的原因:String为字符串常量,而StringBuffer和StringBuilder为字符串变量,及String对象一旦创建之后,字符串对象值是不可更改的,但是后两者的对象值是可以更改的,以下为一段代码实例:
public class Test1 {
public static void main(String[] args) {
String str = "abc";
System.out.println(str); //abc
str = str + "def";
System.out.println(str); //abcdef
}
}
上述这段代码会先输出字符串“abc”,然后输出字符串“abcdef”,表面上看似str这的对象被更改了,其实不然,只是一种假象罢了。JVM对于这几行代码是这样处理的,首先是创建一个String对象str,并把“abc”赋值给str,然后在第五行str = str + "def";
中,其实JVM有创建了一个新的str对象,然后把原来的str对象和“def”拼接后的值赋给了新的str,而原来的str在第四行执行完输出后,就会被JVM的垃圾回收机制(GC)给回收掉了,所以实际上str并没有被更改,这就是前面说的String字符串对象一旦创建就不可更改了,所以Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度会很慢。
而StringBuffer和StringBuilder的对象是变量,对变量的操作就是直接对该对象的值进行更改,而不进行创建和回收的操作,所以速度要比String快很多。
public class Test1 {
public static void main(String[] args) {
String string = "abc" + "def";
StringBuilder stringBuilder = new StringBuilder().append("abc").append("def");
System.out.println(string); //abcdef
System.out.println(stringBuilder.toString());//abcdef
}
}
上述两种输出的结果都是“abcdef”,但是在上述代码中String的速度却比StringBuilder的反映速度要快很多,这是因为String string = "abc" + "def";
这行代码和String str = '"abcdef"
完全是一样的,所以会很快,而如果写成下面这种形式:
String str1="abc";
String str2="def";
String str=str1+str2;
那么JVM中就会像上述所说的那样不断的进行创建、回收操作了,很显然速度就慢下来了。
接下来我们直接测试三者的对比:
package com.test.test;
import com.test.pojo.People;
/**
* @author 一宿君(CSDN : qq_52596258)
* @date 2021-09-24 09:16:07
*/
public class Test1 {
public static void main(String[] args) {
/**
* String操作拼接
*/
String str = "";
Long t1 = System.currentTimeMillis();
for(int i=0;i<10000;i++){
str += i;
}
System.out.println("String字符串拼接消耗的时间:" + (System.currentTimeMillis() - t1)/1000.0 + "S");
/**
* StringBuffer操作拼接
*/
StringBuffer stringBuffer = new StringBuffer();
Long t2 = System.currentTimeMillis();
for(int i=0;i<10000;i++){
stringBuffer.append(i);
}
System.out.println("StringBuffer字符串拼接消耗的时间:" + (System.currentTimeMillis() - t2)/1000.0 + "S");
/**
* StringBuilder操作拼接
*/
StringBuilder stringBuilder = new StringBuilder();
Long t3 = System.currentTimeMillis();
for(int i=0;i<10000;i++){
stringBuilder.append(i);
}
System.out.println("StringBuilder字符串拼接消耗的时间:" + (System.currentTimeMillis() - t3)/1000.0 + "S");
}
}
输出结果:
String字符串拼接消耗的时间:0.198S
StringBuffer字符串拼接消耗的时间:0.002S
StringBuilder字符串拼接消耗的时间:0.001S
知道谁快了吧!!!
在线程安全上,StringBuilder是非线程安全的,而StringBuffer是线程安全的。
如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法都带有synchronized关键字,所有可以保证线程是安全的,但是StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作,所以如果要进行的操作是多线程的,那么就要使用StringBuffer,但是在单线程下,还是建议使用速度较快的StringBuilder。
一个线程访一个对象中的synchronized(this)同步代码块时,其他线程访问该对象将被阻塞。
String和StringBuffer、StringBuilder的主要区别在于String声明的是不可变的对象,每次操作字符串都会生成一个新的String对象,然后将指针指向了新的String对象,而StringBuffer和StringBuilder可以在原有的对象的值上进行操作,所以在经常在改变字符串内容的情况下最好不要使用String。
StringBuffer和StringBuilder最大区别在于,StringBuffer是线程安全的,而StringBuilder是非线程安全的,但StringBuilder的性能却高于StringBuffer,所以在单线程下推荐使用StringBuilder,在多线程下推荐使用StringBuffer。
一起学编程,让生活更随和!如果你觉得是个同道中人,欢迎关注博主公众号:【随和的皮蛋桑】。专注于Java基础、进阶、面试以及计算机基础知识分享。偶尔认知思考、日常水文。