String,StringBuffer与StringBuilder的区别

Posted on 2019-10-02 16:15  苏雅雷斯7988  阅读(120)  评论(0编辑  收藏  举报

String

-不可变类,属性value为不可变数组,即String初始化构造器没有初始容量为16的概念,你定义多少,String中字符数组的长度就是多少,不存在字符数组扩容一说
源码:

其中除了hash,其它属性全被final修饰,
且value只能被初始化一次。这里的value变量其实就是存储了String字符串中的所有字符。
substring方法源码

类似的我们可以看到,String类的concat方法,replace方法,都是内部重新生成一个String对象的
这就是为什么采用String对象频繁的进行拼接,截取,替换操作效率很低下的原因

StringBuilder

-可变字符,内部可变数组,存在初始化StringBuilder对象中字符数组容量为16,存在扩容。
首先看下StringBuilder的构造方法

1:空参数的构造方法


2:自定义初始容量-构造函数

3:以字符串String 作为参数的构造

在参数Str 数组长度的基础上再增加16个字符长度,作为StringBuilder实例的初始数组容量,并将str字符串 append到StringBuilder的数组中。

具体看下父类AbstractStringBuilder的append方法

1:首先判断append的参数是否为null,如果为null的话,这里也是可以append进去的

其中ensureCapacityInternal方法是确保这次append 的时候StringBuilder的内部数组容量是满足的,即这次要append的null字符长度为4,加上之前内部数组中已有的字符位数c之后作为参数执行。

2:如果不为null的话,就获取这次需要append的str的字符长度。紧接着执行是否需要扩容的方法

3:重点看下append方法的关键:String的 getChars方法(从str的0位开始,到str的长度,当前StringBuilder对象的字符数组,当前数组已有的字符长度)
这里的toString方法直接new 一个String对象,将StringBuilder对象的value进行一个拷贝,重新生成一个对象,不共享之前StringBuilder的char[]
没有像String一样去重新new 对象,所以在频繁的拼接字符上,StringBuilder的效率远远高于String类。

StringBuffer

  • 可变字符,内部可变数组,存在初始化StringBuilder对象中字符数组容量为16,存在扩容。
    和StringBuilder一样,也不用多说,重点看下其append方法:
    这里就是在append方法上加了同步锁,来实现多线程下的线程安全。其他的和StringBuilder一致。比StringBuilder多了一个参数toStringCache,其作用是去缓存toString

    StringBuffer的toString方法

其new出来的String对象是会共享同一个char[] 内存的,达到共享的目的。但是StringBuffer只要做了修改,其toStringCache属性值都会置null处理。这也是StringBuffer和StringBuilder的一个区别点。

通过反射实现修改String

通过反射修改String 类的属性value
代码:

public class Main {
    
    public static void main(String[] args) {
        //实践传值修改String
        String string = "我不该生气吗?";
        System.out.println(string);
        modifyString(string);
        System.out.println(string);
    }

    public static void modifyString(String string) {
        try {
            Field field = String.class.getDeclaredField("value");
            field.setAccessible(true);
            field.set(string, field.get("我该如何面对你?"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
}

总结:

String 类不可变,内部维护的char[] 数组长度不可变,为final修饰,String类也是final修饰,不存在扩容。字符串拼接,截取,都会生成一个新的对象。频繁操作字符串效率低下,因为每次都会生成新的对象。

StringBuilder 类内部维护可变长度char[] , 初始化数组容量为16,存在扩容, 其append拼接字符串方法内部调用System的native方法,进行数组的拷贝,不会重新生成新的StringBuilder对象。非线程安全的字符串操作类, 其每次调用 toString方法而重新生成的String对象,不会共享StringBuilder对象内部的char[],会进行一次char[]的copy操作。

StringBuffer 类内部维护可变长度char[], 基本上与StringBuilder一致,但其为线程安全的字符串操作类,大部分方法都采用了Synchronized关键字修改,以此来实现在多线程下的操作字符串的安全性。其toString方法而重新生成的String对象,会共享StringBuffer对象中的toStringCache属性(char[]),但是每次的StringBuffer对象修改,都会置null该属性值。

Copyright © 2024 苏雅雷斯7988
Powered by .NET 8.0 on Kubernetes