美文网首页
String,StringBuffer,StringBuilde

String,StringBuffer,StringBuilde

作者: 小刘财商 | 来源:发表于2020-04-03 16:41 被阅读0次

    本篇包含内容:

        面试题:String,StringBuffer,StringBuilder的区别?

        String常见问题解析:

            ①String特点?

            ②为什么String长度不可变?

            ③为什么String不是基本数据类型,但是可以不用new创建对象?

            ⑤String两种创建方式(String a="abc"和 String a=new String("abc"))区别?

            ⑥String的拼接问题

            ⑦String的equals和==

            ⑧String设计成不可变的原因?

        StringBuilder源码解析

            ①StringBuilder为什么可变?

            ②StringBuilder扩容方式?

        StringBuffer源码解析

    面试常见问题:String,StringBuffer,StringBuilder的区别?

    这个问题的答案:

    1,从运行速度上看:StringBuilder > StringBuffer > String

    2,从长度上看:StringBuilder 和StringBuffer 是字符串变量,长度可变,String 创建后不可更改

    3,从线程安全上看:StringBuilder是线程不安全的,StringBuffer是线程安全的 

    4,结论:

        当操作String时,需要不断创建和回收对象

        String 适合少量字符串操作情况

        StringBuffer:适用于多线程在字符缓冲区进行大量操作情况

        StringBuilder:适用于在单线程在字符缓冲区进行大量操作的情况

    如果你是一个刚学java的小白,只是为了应付下面试,把上面的答案背一背就可以了。

    如果你被生活所迫 发自内心想了解更多,请继续往下看(以下会涉及到栈丶堆相关内容,不在本篇文章重点讲述,如还不了解,请自行百度)


    ------------------------------------------------------重点之String--------------------------------------------------

    1,String特点?

        ①String不可继承,支持序列化,支持自然排序

     看源码:

        String 是由final修饰的类,且实现了Serializable和Comparable接口

        ②String可以采用直接赋值的形式进行操作,这一点像基本数据类型的赋值操作一样。范例:String str = "hello";

        ③String1.equals(String2)比较的是两个String字符串的值,一般类是比较的引用

        ④String底层实现用的char[]  数组

        ⑤String类的设计使用了一个共享设计模式

            有的看官老爷就说了:弟弟,能说点人话吗?

            哥哥,别急呀(手动撒娇)

    撒娇ing

        在JVM中,为了减少字符串对象的重复创建,维护了一块特殊的内存空间,这块内存就被称为字符串常量池。为啥要维护这么一块空间,还是因为String常用啊,因为常用,所以难免会有相同的字符串,没有必要为相同的字符串浪费两块内存,如果设计成共享的,那么会节省资源。

    ⑥字符串一旦创建,对他任何操作都不能改变

    2,为什么String长度不可变?

    看源码:

    String 类是通过char数组保存字符串的,且char数组也是由final修饰的,final修饰的变量,不可更改所以长度固定的

    3,为什么String不是基本数据类型,但是可以不用new创建对象?

        上边我们说了一个概念:字符串常量池

        那么什么是字符串常量池呢?

        ( 额...固定搭配,背下来....)

        开玩笑啦,其实你可以把字符串常量池当做图书馆,就是里边书都是公用的共享的,你想用你拿去用,他想用拿去用,一万个人都想用这本书,都可以拿去用,但是书只需要买一本就够了,这样可以大大节省资源,省下钱做啥不好。字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价。JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化。为了减少在JVM中创建的字符串的数量,字符串类维护了一个字符串池。

        了解这些,这个问题原因就很容易理解了。每当代码创建字符串常量时,JVM会首先检查字符串常量池。如果字符串已经存在池中, 就返回池中的实例引用。如果字符串不在池中,就会实例化一个字符串并放到池中。所以String虽然是引用类型,依然可以不用new创建对象,说白了还是这小伙子常用呗。

    4,String两种创建方式(String a="abc"和 String a=new String("abc"))区别?

        看官老爷又发话了:您可真是傻,这区别还不明显吗?一个用new一个不用new,就这?

        要是这么简单,我怎么好意思在各位聪明的看官老爷面前显摆呢?

        第一种方式,先去常量池中遍历,如果有"abc"字符串,则直接返回当前地址,如果没有,则先在常量池中创建"abc",然后返回其地址,此过程创建零个或一个对象

        第二种方式,先在池中遍历,,如果有"abc"字符串,则进行下一步,如果没有,则先在常量池中创建"abc",然后在堆中创建String对象,指向池中"abc"的地址,此过程创建一个或两个对象,一定会创建对象

        看不懂?没关系,

        我们通过一个笔试题来看String的创建过程(敲黑板,划重点,常考):

        String s1="javaEE"; 

        String s2="javaEE";

        String s3=new String("javaEE");

        String s4=new String("javaEE");      

        System.out.println(s1==s2);

        System.out.println(s1==s3);

        System.out.println(s1==s4);

        System.out.println(s3==s4);

        字符串s1,s2,s3和s4的创建过程是什么样的呢?看图

        s1:局部变量是存在栈中的,所以当执行到String s1这一步时,会在栈内存中开辟空间,存放s1,下一步s1="javaEE", 如果是通过字符串这种直接赋值的形式,会先检查字符串常量池是否有"javaEE"这个字符串,因为这是第一次创建,在字符串常量池中不存在,所以先在常量池中创建"javaEE"对象,这里假设"javaEE"的地址是0x1212,s1中存放的地址是0x1212;

        s2:与s1创建过程一样,先在栈中存放s2,然后去常量池中找是否有"javaEE"字符串,创建s1时已经创建过了,所以直接返回此地址0x1212,那么s2的地址与s1相同

        s3:同样先执行String s3,在栈中开辟内存存放s3,下一步s3=new String();要在堆中开辟一块内存存放String对象(此时假设对象地址是0x8899),所以s3中存放地址为0x8899,然后将常量池中对应字符串的地址赋值给堆中String对象,那么String 的value值为0x1212;

        s4:与s3过程相同,只是new对象肯定会重新创建对象,所以s4的地址值不能指向0x8899,而是0x7777;

    讲到这里,答案就很明显了,就第一个为true;

    5,String的拼接问题

        ①两个字符串直接拼接,String a="a"+"b";使用最多的情况就是字符串太长,一行放不下,需要拼接,这种是直接在常量池中创建"ab",编译期间就执行完毕了,效率高

        ②对于 String a="a";String b="b"; a+b ;的情况则是运行期执行,反编译后:new StringBuilder(String.valueOf(a))).append(b).toString(); 此时在堆中创建两个对象

        ③String的concat(String str) 方法,每次调用都会重新在常量池中创建新对象,并将新对象地址赋给String,代码实现:创建char数组,长度是两个字符串长度和,将两个字符串复制到数组中,并创建新对象返回,所以调用几次,创建几个对象

    6,String的equals和==

        默认情况下equals和==都是比较地址,但是String重写了equals方法,所以String比较的是值,看一下equals()方法源码:

        源码分析:

        1,若A==B,则是同一个String对象,返回true

        2,若对象都是String类型继续下一步,否则返回false

        3,判断A,B长度是否一样,不一样返回false

        4,逐个字符比较,若有不同,返回false

       equals的特点:

        自反性:x.equals(x)一定返回true

        对称性:x.equals(y)为true,y.equals(x)一定为true

        传递性:x.equals(y)=true,y.equals(z)=true,那么x.equals(z)=true

        一致性:如果参与比较对象没变,对象比较结果也不会变

        非空性:任何非空引用值x,x.equals(null)一定为false

       7,String设计成不可变的原因?

        面试时或许会问到,还能有啥原因?常用呗(小声bb)。当然肯定不能回答这个       

            ①字符串常量池的需要,字符串常量池是为了提升效率和减少内存分配,编程大量时间都在处理字符串,所以字符串很大概率出现重复情况,String不可变,常量池更容易被管理和优化.

            ②安全性考虑,因为字符串广泛用于参数,如网络连接,打开文件等,如果String可变,网络连接等将被改变,导致一系列安全问题

             ③java中常用到字符串的hash码,String不变,hash码唯一,不用每次用的时候重新计算,提高效率

    --------------------------------------------------------StringBuilder----------------------------------------------------

    1,StringBuilder的源码分析(为什么长度可变?)

        先来看默认构造方法:

        默认构造方法调用了父类构造方法,我们点进去看看如下图:

    父类构造方法创建一个长度为16的char数组,原来StringBuilder用的也是char数组,那看官老爷就要问了,为什么String用的char数组,长度不可变,你也用char数组你就能变,就因为你名长吗?当然不是,StringBuilder的char数组不是final修饰的,所以它是可以被重新赋值的。看官老爷可能一脸懵逼,咋回事?恩...我也迷糊了,那我们先写个测试代码,然后调用拼接方法试试吧

    先创建个StringBuilder对象sb,恩...不太好听(成大事不拘小节),然后调用append方法拼接,并没有重新给sb赋值,但是sb的内容变了,很神奇。构造方法没有什么特殊的,那点开append方法看一看:


    调用父类append()方法,并返回当前对象,点父类append()看看具体做了哪些事情:

    先看str.getChars()这行代码,是将str的字符复制到value数组中,从value数组的count处存放,说白了就是将str字符拼接到原字符的后边,那么我们知道StringBuilder是通过char[] value存储的,最初长度是16位,如果长度超过16位,会如何处理呢?ensureCapacityInternal()方法就是处理扩容问题的,我们点进去看一看:

    minimumCapacity是拼接后字符串长度,value.length是存放当前字符串的数组长度,如果拼接后长度大于当前数组长度,则将当前数组拷贝到新数组并赋值给当前value。至此,我们应该知道为什么StringBuilder是可变的,先创建一个16长度数组,拼接内容就往数组里添加字符,当超过16就给value赋值新的char数组。这时会有人有疑问,那我新创建数组到底创建多大的呢?既然要扩容我最开始创建个很大的不就好了,省的这么麻烦?别急,慢慢看,具体如何扩容呢,新数组多长呢,秘密全在newCapacity()这个方法中,我们先点进去看看:

    第一行将当前数组容量*2再加2,如果新的容量比拼接后的字符串长度要小,那直接将新容量改为拼接后的字符串长度.最后一行,当新容量小于等于0或者大于MAX_ARRAY_SIZE,基本很少发生,故先不考虑。

    --------------------------------------------------------StringBuffer----------------------------------------------------

    StringBuffer内容与StringBuilder基本相同,扩容方式一样,唯一不同的是StringBuffer的拼接方法是同步的,看下图,所以是线程安全的

        能看到这里,我发现你是真的爱我,那我也想对你说一句:

        把耳朵伸过来

        .

        .

        .

        .

        .   

        .

        .

        如果有用,麻烦点个赞呗,软件行业的小学生,Android上的弟弟在这里跪谢了


        END

    相关文章

      网友评论

          本文标题:String,StringBuffer,StringBuilde

          本文链接:https://www.haomeiwen.com/subject/vfhouhtx.html