3分钟快速了解String、StringBuffer和StringBuilder的区别

前言

  在java学习中,String作为一个我们常用的数据类型,是我们经常性见到的。但是它和StringBuffer、StringBuilder的区别以及优缺点是什么?相信很多人无法说个大概,博主也是如此。那么我们就一起整理一下他们的区别。

区别

  1. 继承的类不同。String继承的是java.lang.String,而StringBuffer和StringBuilder继承的是AbstractStringBuilder(一个抽象类);
  2. String不可变,StringBuffer和StringBuilder可变。因为String是一个字符串常量,而StringBuilder和StringBuffer均为字符串变量。
    查看String的源码,你会发现String是由一个final修饰的字节数组成的,所以String是一个常量不可变。
    这里写图片描述
    这里写图片描述

  3. String和StringBuffer是线程安全的,而StringBuilder是非线程安全的。由于String是一个常量,毫无疑问它是安全的。StringBuffer线程是安全的原因是,你点击看到它的源码里面是含有很多synchronized关键字修饰的方法,synchronized关键字是为了实现线程同步避免造成死锁,避免多个线程同时调用同一资源,所以它的线程也是安全的。
    这里写图片描述
    而同样点击进入StringBuilder的源码,你会发现它的方法里面没有这个synchronized关键字,所以它的线程是不安全的。
    这里写图片描述
    下面我们通过一个多线程案例来看一下StringBuffer和StringBuiler的区别

public void getStringBuilerLength{
     List<Thread> threadList=new ArrayList<Thread>();
     StringBuilder stringBuilder=new StringBuilder("a");
     for(int i=1;i<5000;i++){
         Thread t=new Thread(){
             @Override
             public void run() {
                 stringBuilder.append("x");
             }
         };
         threadList.add(t);
         t.start();

     }

     for (Thread item: threadList) {
         try {
             item.join();
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
     }
     System.out.println("lengthOne="+stringBuilder.length());
 }

public void getStringBuilerLength(){
    List<Thread> threadList=new ArrayList<Thread>();
    StringBuffer stringBuffer=new StringBuffer("a");
    for(int i=1;i<5000;i++){
        Thread t=new Thread(){
            @Override
            public void run() {
                stringBuffer.append("x");
            }
        };
        threadList.add(t);
        t.start();
    }

    for (Thread item: threadList) {
        try {
            item.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    System.out.println("lengthTwo="+stringBuffer.length());
}

调用上面的两个方法,我们会发现同样是给字符串变量追加4999个“ x”字符,那么它们的长度都应该是5000
但是运行后发现有时StringBuilder的长度并不是5000,这是因为StringBuilder的方法里面没有synchronized关键字实现线程同步,所以使用多线程时,某个时刻可能会有多个线程对它的变量stringBuilder进行访问,所以最后没有达到预料效果。这就是StringBuilder线程不安全的原因。
这里写图片描述
4. 执行速度不同:StringBuilder > StringBuffer > String。我们一起来测试它们的执行速度

    private static void getStringTime(){
        long start=System.currentTimeMillis();
        String str="a";
        for(int i=1;i<10000;i++){
            str+=i;
        }
        long stop=System.currentTimeMillis();
        System.out.println("String time="+(stop-start));
    }

    private static void getStringBufferTime(){
        long start=System.currentTimeMillis();
        StringBuffer str=new StringBuffer("aa");
        for(int i=1;i<10000;i++){
           str.append(i);
        }
        long stop=System.currentTimeMillis();
        System.out.println("StringBuffer time="+(stop-start));

    }

    private static void getStringBuilderTime(){
        long start=System.currentTimeMillis();
        StringBuilder str=new StringBuilder("aa");
        for(int i=1;i<10000;i++){
            str.append(i);
        }
        long stop=System.currentTimeMillis();
        System.out.println("StringBuilder time="+(stop-start));

    }

运行时间为
这里写图片描述
实践出真知,由此可见我们的结论是正确的。那么具体的原因是什么呢?
我们从第3点知道,StringBuffer线程安全,每个方法都有synchronized同步阻塞,所以操作速度受影响。而StringBuilder没有同步阻塞的限制,所以操作速度最快。同时于String中进行操作时都会新建一个char[]数组代替原来的char[]数组,所以速度最慢 。我们再举一个案例来说明一下:

String str="asd";
System.out.println(str);
str=str+"fg";
System.out.println(str);
//结果是:
//asd
//asdfg

在JVM中对于上面这几行代码是这样处理的,首先创建一个String对象str,并赋值“asd”给str,然后在第三行中,其实JVM又创建了一个新的对象也名为str,然后再把原来的str的值和“gh”串联起来并赋值给新的str,而原来的str对象就会被JVM的垃圾回收机制(GC)给回收掉了,所以,str实际上并没有被更改,也就是说String对象一旦创建之后就不可更改了。所以,Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢。

总结

通过这些区别的分析,相信大家对String、StringBuffer和StringBuilder的区别有一定的了解,我们来总结一下它们的具体用法。
1. 操作少量的数据用 String
2. 线程操作字符串缓冲区 下操作大量数据用 StringBuilder
3. 多线程操作字符串缓冲区下操作大量数据用 StringBuffer

猜你喜欢

转载自blog.csdn.net/Black1499/article/details/81635288