java中string、stringbuilder、stringbuffer三者的区别性能比较及使用场景

探索java中string、stringbuilder、stringbuffer三者的区别性能比较及使用的场景

相信String这个类是Java中使用得最频繁的类之一,并且又是各大公司面试喜欢问到的地方,今天就来和大家一起学习一下String、StringBuilder和StringBuffer这几个类,分析它们的异同点以及了解各个类适用的场景

  • 首先了解String类
    想要了解一个类,最好的办法就是看这个类的实现源代码,String类的实现在\jdk1.6.0_14\src\java\lang\String.java 文件中,打开这个类文件就会发现String类是被final修饰的:

    public final class String
        implements java.io.Serializable, Comparable<String>, CharSequence
    {
        /** The value is used for character storage. */
        private final char value[];
        /** The offset is the first index of the storage that is used. */
        private final int offset;
        /** The count is the number of characters in the String. */
        private final int count;
        /** Cache the hash code for the string */
        private int hash; // Default to 0
        /** use serialVersionUID from JDK 1.0.2 for interoperability */
        private static final long serialVersionUID = -6849794470754667710L;
        ......
    }
    

    从上面可以看出:
      String类是final类,也即意味着String类不能被继承,并且它的成员方法都默认为final方法。在Java中,被final修饰的类是不允许被继承的,并且该类中的成员方法都默认为final方法。在早期的JVM实现版本中,被final修饰的方法会被转为内嵌调用以提升执行效率。而从Java SE5/6开始,就渐渐摈弃这种方式了。因此在现在的Java SE版本中,不需要考虑用final去提升方法调用效率。只有在确定不想让该方法被覆盖时,才将方法设置为final。
    注:对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象。

  • 深入理解String、StringBuffer、StringBuilder

    当对字符串进行修改的时候,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象,String则每次修改时都需要开辟一个新的内存。

  1. String
    String类不可变,内部维护的char[]数组长度不可变,为final修饰,String类也是final修饰,不存在扩容。字符串拼接,截取,都会生成一个新的对象。频繁操作字符串效率低下,因为每次都会生成新的对象。
  2. StringBuilder
    StringBuilder的方法不是线程安全的(不能同步访问)但是执行速度快,类内部维护可变长度char[] , 初始化数组容量为16,存在扩容,其append拼接字符串方法内部调用System的native方法,进行数组的拷贝,不会重新生成新的StringBuilder对象。非线程安全的字符串操作类,其每次调用toString方法而重新生成的String对象,不会共享StringBuilder对象内部的char[],会进行一次char[]的copy操作。
  3. StringBuffer
    StringBuffer 的方法是线程安全的相比StringBuilder执行速度则略慢,在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类,类内部维护可变长度char[],基本上与StringBuilder一致,但其为线程安全的字符串操作类,大部分方法都采用了Synchronized关键字修改,以此来实现在多线程下的操作字符串的安全性。其toString方法而重新生成的String对象,会共享StringBuffer对象中的toStringCache属性(char[]),但是每次的StringBuffer对象修改,都会置null该属性值。
    String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,这样不仅效率低下,而且大量浪费有限的内存空间。我们来看一下这张对String操作时内存变化的图
  • String字符串常量原理
    在这里插入图片描述

    从图片中我们可以看到,初始的String值为“hello”,然后在字符串后面加上新的字符串“world”,这个过程是需要重新在栈堆内存中开辟内存空间的,最终得到了“hello world”字符串也相应的需要开辟内存空间,这样短短的两个字符串,需要开辟三次内存空间,对内存空间是很大的浪费,因此需要对其进行性能优化。为了应对资源的高效利用谷歌新引入两个类——StringBuffer类和StringBuild类来对此种变化字符串进行处理

  • 相同场景三者的性能差别

    public static void testString () {
        String s="";
        long begin = System.currentTimeMillis();
        for(int i=0; i<5000; i++){
            s += "java";
        }
        long over = System.currentTimeMillis();
        System.out.println("操作"+s.getClass().getName()+"类型使用的时间为:"+(over-begin)+"毫秒");
    }
         
    public static void testStringBuffer () {
        StringBuffer sb = new StringBuffer();
        long begin = System.currentTimeMillis();
        for(int i=0; i<5000; i++){
            sb.append("java");
        }
        long over = System.currentTimeMillis();
        System.out.println("操作"+sb.getClass().getName()+"类型使用的时间为:"+(over-begin)+"毫秒");
    }
         
    public static void testStringBuilder () {
        StringBuilder sb = new StringBuilder();
        long begin = System.currentTimeMillis();
        for(int i=0; i<5000; i++){
            sb.append("java");
        }
        long over = System.currentTimeMillis();
        System.out.println("操作"+sb.getClass().getName()+"类型使用的时间为:"+(over-begin)+"毫秒");
    }
    

    做了一个实验,用for循环执行5000次,每一次循环都相加字符串内容,使用三者不同的字符串对象进行创建,下图是执行完之后耗用时长对比
    在这里插入图片描述
    从结果上明显看得出三者的性能差别
    String、StringBuilder、StringBuffer三者的执行效率:StringBuilder > StringBuffer > String
    但并非所以场景都适用StringBuffer效率最高的,这三者各有利弊,下面说明三者一般在什么场景下使用。

  • String、StringBuffer、StringBuilder三者是使用场景

    使用String类的场景:在字符串不经常变化的场景中可以使用String类,例如常量的声明、少量的变量运算。

    使用StringBuffer类的场景:在频繁进行字符串运算(如拼接、替换、删除等),并且运行在多线程环境中,因为StringBuffer是线程安全,则可以考虑使用StringBuffer。

    使用StringBuilder类的场景:在频繁进行字符串运算(如拼接、替换、和删除等),并且运行在单线程的环境中,因为StringBuilder是线程不安全的,则可以考虑使用StringBuilder。

    总结:
    (1)如果要操作少量的数据使用 String;
    (2)多线程场景下操作字符串缓冲区下操作大量数据使用 StringBuffer;
    (3)单线程场景下操作字符串缓冲区下操作大量数据使用 StringBuilder。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dlei东

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值