美文网首页
String,StringBuffer与StringBuilde

String,StringBuffer与StringBuilde

作者: Quillagua | 来源:发表于2020-04-08 14:46 被阅读0次

    String

    String在java编程中广泛应用,首先从源码进行分析


    image.png

    从这我们可以得知,String底层是一个final类型的字符数组,所以String的值是不可变的,每次对String的操作都会生成新的String对象,造成内存浪费
    而StringBuffer和StringBuilder就不一样了,他们两都继承了AbstractStringBuilder抽象类,从AbstractStringBuilder抽象类中我们可以看到


    image.png

    他们的底层都是可变的字符数组,所以在进行频繁的字符串操作时,建议使用StringBuffer和StringBuilder来进行操作。
    接着看一下他们的继承结构以及部分源码实现


    image.png image.png image.png

    从这三张图我们不难得知:StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。

    再详细说一下String
    String并不是基本数据类型,而是一个对象。字符串为对象,那么在初始化之前,它的值为null,到这里就有必要提下””、null、new String()三者的区别。null 表示string还没有new ,也就是说对象的引用还没有创建,也没有分配内存空间给他,而””、new String()则说明了已经new了,只不过内部为空,但是它创建了对象的引用,是需要分配内存空间的。
    java的虚拟机在内存中开辟出一块单独的区域,用来存储字符串对象,这块内存区域被称为字符串缓冲池。那个java的字符串缓冲池是如何工作的呢?

    image.png
    String a = "abc";
    String b = "abc";
    String c = new String("xyz");
    

    例如上边的代码:

    String a = "abc";
    创建字符串的时候先查找字符串缓冲池中有没有相同的对象,如果有相同的对象就直接返回该对象的引用,如果没有相同的对象就在字符串缓冲池中创建该对象,然后将该对象的应用返回。对于这一步而言,缓冲池中没有abc这个字符串对象,所以首先创建一个字符串对象,然后将对象引用返回给a。

    String b = "abc";
    

    这一句也是想要创建一个对象引用变量b使其指向abc这一对象。这时,首先查找字符串缓冲池,发现abc这个对象已经有了,这是就直接将这个对象的引用返回给b,此时a和b就共用了一个对象abc,不过不用担心,a改变了字符串不会影响b,因为字符串都是常量,一旦创建就没办法修改了,除非创建一个新的对象。

    String c = new String("xyz");
    

    查找字符串缓冲池发现没有xyz这个字符串对象,于是就在字符串缓冲池中创建了一个xyz对象然后再将引用返回。
    从上边的分析可以看出,当new一个字符串时并不一定是创建了一个新的对象,有可能是与别的引用变量共同使用了同一个对象。下面看几个常见的有关字符串缓冲池的问题。

    创建了几个对象

    String a = "abc";
    String b = "abc";
    String c = new String("xyz");
    String d = new String("xyz");
    String e = "ab" + "cd";
    

    这个程序与上边的程序比较相似,我们分比来看一下:

    String a = “abc”;这一句由于缓冲池中没有abc这个字符串对象,所以会创建一个对象;
    
    String b = “abc”;由于缓冲池中已经有了abc这个对象,所以不会再创建新的对象;
    
    String c = new String(“xyz”);由于没有xyz这个字符串对象,所以会首先创建一个xyz的对象,然后这个字符串对象由作为String的构造方法,在内存中(不是缓冲池中)又创建了一个新的字符串对象,所以一共创建了两个对象;
    
    String d = new String(“xyz”);缓冲池中已有该字符串对象,则缓冲池中不再创建该对象,然后会在内存中创建一个新的字符串对象,所以只创建了一个对象;
    
    String e = ”ab” + ”cd”;由于常量的值在编译的时候就被确定了。所以这一句等价于String e = ”abcd”;所以创建了一个对象;
    

    所以创建的对象的个数分别是:1,0,2,1,1。

    到底相等不相等

    我们在学习java时就知道两个字符串对象相等的判断要用equal而不能使用==,但是学习了字符串缓冲池以后,应该知道为什么不能用==, 什么情况下==,和equal是等价的,首先,必须知道的是,equal比较的是两个字符串的值是否相等,而==比较的是两个对象的内存地址是否相等,下面我们就通过几个程序来看一下。

    实例一:

    public static void main(String[] args) { 
        String s1 = "Monday"; 
        String s2 = "Monday"; 
        if (s1 == s2) {
            System.out.println("s1 == s2"); 
        }
        else {
            System.out.println("s1 != s2"); 
        }
    }
    输出结果:
    
    s1 == s2
    

    分析:通过上边的介绍字符串缓冲池,我们知道s1和s2都是指向字符串缓冲池中的同一个对象,所以内存地址是一样的,所以用==可以判断两个字符串是否相等。

    实例二:

    public static void main(String[] args) { 
        String s1 = "Monday"; 
        String s2 = new String("Monday"); 
        if (s1 == s2) {
            System.out.println("s1 == s2"); 
        }
        else {
             System.out.println("s1 != s2"); 
        }
        if (s1.equals(s2)) {
            System.out.println("s1 equals s2"); 
        }
        else {
            System.out.println("s1 not equals s2"); 
        }
    } 
    输出结果:
    
    s1 != s2
    s1 equals s2
    

    分析:由上边的分析我们知道,String s2 = new String(“Monday”);这一句话没有在字符串缓冲池中创建新的对象,但是会在内存的其他位置创建一个新的对象,所以s1是指向字符串缓冲池的,s2是指向内存的其他位置,两者的内存地址不同的。

    实例三:

    public static void main(String[] args) { 
        String s1 = "Monday"; 
        String s2 = new String("Monday"); 
        s2 = s2.intern(); 
        if (s1 == s2) {
            System.out.println("s1 == s2"); 
        }
        else {
            System.out.println("s1 != s2"); 
        }
        if (s1.equals(s2)) {
            System.out.println("s1 equals s2"); 
        }
        else {
            System.out.println("s1 not equals s2"); 
        }
    }
    输出结果:
    
    s1 == s2
    s1 equals s2
    

    分析:先来说说intern()这个方法的作用吧,这个方法的作用是返回在字符串缓冲池中的对象的引用,所以s2指向的也是字符串缓冲池中的地址,和s1是相等的。

    实例四:

    public static void main(String[] args) { 
     
        String Monday = "Monday";  
        String Mon = "Mon";  
        String  day = "day";  
        System.out.println(Monday == "Mon" + "day");  
        System.out.println(Monday == "Mon" + day);  
     
    }
    

    输出结果:

    true
    false
    

    分析:第一个为什么等于true我们已经说过了,因为两者都是常量所以在编译阶段就已经能确定了,在第二个中,day是一个变量,所以不能提前确定他的值,所以两者不相等,从这个例子我们可以看出,只有+连接的两边都是字符串常量时,引用才会指向字符串缓冲池,都则都是指向内存中的其他地址。

    实例五:

    public static void main(String[] args) { 
     
        String Monday = "Monday";  
        String Mon = "Mon";  
        final String  day = "day";  
        System.out.println(Monday == "Mon" + "day");  
        System.out.println(Monday == "Mon" + day);  
     
    }
    输出结果:
    true
    true
    

    分析:加上final后day也变成了常量,所以第二句的引用也是指向的字符串缓冲池。
    原文链接:https://blog.csdn.net/TTTZZZTTTZZZ/java/article/details/84892985

    相关文章

      网友评论

          本文标题:String,StringBuffer与StringBuilde

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