Java提高篇 —— String缓冲池

一、String缓冲池

       首先我们要明确,String并不是基本数据类型,而是一个对象,并且是不可变的对象。查看源码就会发现String类为final型的(当然也不可被继承),而且通过查看JDK文档会发现几乎每一个修改String对象的操作,实际上都是创建了一个全新的String对象。

       字符串为对象,那么在初始化之前,它的值为null,到这里就有必要提下””、null、new String()三者的区别。null 表示string还没有new ,也就是说对象的引用还没有创建,也没有分配内存空间给他,而””、new String()则说明了已经new了,只不过内部为空,但是它创建了对象的引用,是需要分配内存空间的。

       java的虚拟机在内存中开辟出一块单独的区域,用来存储字符串对象,这块内存区域被称为字符串缓冲池。那个java的字符串缓冲池是如何工作的呢?

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一个字符串时并不一定是创建了一个新的对象,有可能是与别的引用变量共同使用了同一个对象。下面看几个常见的有关字符串缓冲池的问题。

扫描二维码关注公众号,回复: 2785789 查看本文章

二、创建了几个对象

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

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

       1、String a = “abc”;这一句由于缓冲池中没有abc这个字符串对象,所以会创建一个对象

       2、String b = “abc”;由于缓冲池中已经有了abc这个对象,所以不会再创建新的对象

       3、String c = new String(“xyz”);由于没有xyz这个字符串对象,所以会首先创建一个xyz的对象,然后这个字符串对象由作为String的构造方法,在内存中(不是缓冲池中)又创建了一个新的字符串对象,所以一共创建了两个对象

       4、String d = new String(“xyz”);缓冲池中已有该字符串对象,则缓冲池中不再创建该对象,然后会在内存中创建一个新的字符串对象,所以只创建了一个对象

       5、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也变成了常量,所以第二句的引用也是指向的字符串缓冲池。

猜你喜欢

转载自blog.csdn.net/wangzhongshun/article/details/81708647