共同点:
- 都是处理string类型的数据 这三个类都是来自java.lang中
- String和StringBuffer使用的是char数组为数据结构
- StringBuilder在String上直接进行操作
不同点
- String,StringBuilder线程不安全
- StringBuffer线程安全
- String多有操作都是基于新对象,返回新对象
- StringBuffer,StringBuilder所有操作都在现对象中操作
1.String
结构图
1)String类是final类,也即意味着String类不能被继承,并且它的成员方法都默认为final方法。在Java中,被final修饰的类是不允许被继承的,并且该类中的成员方法都默认为final方法。在早期的JVM实现版本中,被final修饰的方法会被转为内嵌调用以提升执行效率。而从Java
SE5/6开始,就渐渐摈弃这种方式了。因此在现在的Java
SE版本中,不需要考虑用final去提升方法调用效率。只有在确定不想让该方法被覆盖时,才将方法设置为final。
2)上面列举出了String类中所有的成员属性,从上面可以看出String类其实是通过char数组来保存字符串的。
> 无论是substring、concat还是replace操作都不是在原有的字符串上进行的,而是重新生成了一个新的字符串对象。也就是说进行这些操作后,最原始的字符串并没有被改变。
在这里要永远记住一点:
“对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象”。
2.StringBuffer
3.StringBuilder
下面对上面的执行结果进行一般性的解释:
1)对于直接相加字符串,效率很高,因为在编译器便确定了它的值,也就是说形如"I"+“love”+“java”;
的字符串相加,在编译期间便被优化成了"Ilovejava"。这个可以用javap -c命令反编译生成的class文件进行验证。
对于间接相加(即包含字符串引用),形如s1+s2+s3; 效率要比直接相加低,因为在编译器不会对引用变量进行优化。
2)String、StringBuilder、StringBuffer三者的执行效率:
StringBuilder > StringBuffer > String
当然这个是相对的,不一定在所有情况下都是这样。
比如String str = “hello”+ "world"的效率就比 StringBuilder st = new
StringBuilder().append(“hello”).append(“world”)要高。
因此,这三个类是各有利弊,应当根据不同的情况来进行选择使用:
当字符串相加操作或者改动较少的情况下,建议使用 String str="hello"这种形式;
当字符串相加操作较多的情况下,建议使用StringBuilder,如果采用了多线程,则使用StringBuffer。
参考:https://www.cnblogs.com/dolphin0520/p/3778589.html
前面的博客已经写得很详细啦,我就做个小实验,证明下线程安全问题吧:
public class StringThreadSecurity {
public String string = new String();
public StringBuilder stringBuilder = new StringBuilder();
public StringBuffer stringBuffer = new StringBuffer();
public static void main(String[] args) throws InterruptedException {
StringThreadSecurity test=new StringThreadSecurity();
ExecutorService executor = Executors.newFixedThreadPool(3);
CountDownLatch countDownLatch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
MyThreadTestString myThread = new MyThreadTestString(countDownLatch,test);
executor.execute(myThread);
}
countDownLatch.await();
executor.shutdown();
System.out.println("string "+test.string.length());
System.out.println("stringBuilder "+test.stringBuilder.length());
System.out.println("stringBuffer "+test.stringBuffer.length());
}
}
class MyThreadTestString extends Thread {
private CountDownLatch countDownLatch;
private StringThreadSecurity test;
public MyThreadTestString(CountDownLatch countDownLatch,StringThreadSecurity test) {
this.countDownLatch = countDownLatch;
this.test=test;
}
public void run() {
try {
for (int i = 0; i < 1000; i++) {
test.string = test.string + 'a';
test.stringBuilder.append('a');
test.stringBuffer.append('a');
}
} finally {
countDownLatch.countDown();
}
}
}
3个线程循环1000次,字符创长度应该是3000个,可以看出StringBuffer的确是线程安全的。