5.常量池解析

程序運行過程當中,會把一些常量放在一塊區域中,以便下次使用時不用建立之間使用該區域的值,該區域就是常量池,常量池主要用於存放編譯期生成的各類字面量和符號引用。常量池在jdk1.6及以前是放在永久代中,即方法區(元空間)中,在1.7中,常量池仍在永久代中,但字符串常量池移到了堆中,在1.8,永久代被移除,常量池就被放在了方法區中。字符串常量池在堆中。java

字符串常量池

字符串對象的建立方式有三種:app

String s1 = 「asd」;jvm

String s2 = new String(「asd」);優化

String s3 = s1.intern();ui

第一種:
未命名文件.pngspa

String a1 = "asd";
        String a2 = "asd";
        String a3 = "a"+"sd";
        String a4 = "a";
        String a5 = a4 +"sd";
        System.out.println(a1==a2);//true
        System.out.println(a1==a3);//true
        System.out.println(a1==a5);//false

String a1 = 「asd」;3d

這種方式,jvm會直接在堆中的常量池中建立一個「asd」字符串對象(前提是原來沒有),而後返回該對象的引用,若是下次有使用該字符串,則不用建立,直接返回該引用。code

狀況以下:對象

String a2 = 「asd」;a2返回的與a1是同一個對象引用,a1與a2是相等的。blog

String a3=「a」+」s」+」d」;程序在運行過程當中在靜態編譯階段,虛擬機就會對代碼進行優化,對於那些能夠肯定的變量值會直接優化使用,也就是等價於a3=「asd」;因此是與a1和a2相等的,。

String a4 = 「a」;

String a5 = a4+「sd」,這時,a5=「asd」,可是a5與上述a一、a二、a3不相等,由於a5在建立的時候,存在a4的引用,而引用的值在編譯期是沒法肯定的,只有在程序運行期來動態分配並將鏈接後的新地址賦給a5。因此沒法進行優化,即在字符串常量池中又產生了一條新的字符串。其運行原理以下

StringBuilder temp = new StringBuilder();

temp.append(a).append(sd);

String a5 = temp.toString();

會返回一個新的引用。

第二種:
未命名文件.png

String b1 = new String("asd2");
        String b2 = new String("asd2");
        String b3 = new String("a")+new String("sd2");
        System.out.println(b1==b2);  //false
        System.out.println(b1==b3);  //false

String b1 = new String (「asd」);

這種方式,jvm首先會去字符串常量池中去建立一個「asd「字符串對象1(前提是原來沒有),而後再去常量池外堆內建立一個「asd」的對象2,而後把對象2的引用返回。

String b2 = new String(「asd」),這時常量池中已經存在,就不進行建立,直接在常量池外堆內建立一個新的「asd」對象3,並返回對象3,此時b1和b2指向的不是一個對象,因此並不相等。

第三種

String c1 = new String("asd");

String c2 = c1.intern();

String c1 = new String("asd3");
        String c2 = c1.intern();
        String c3 = c1.intern();
        System.out.println(c1==c2);//false
        System.out.println(c2==c3);//true

        String d1 = new StringBuffer("a").append("sd4").toString();
        String d2 = d1.intern();
        System.out.println(d1==d2);//true(1.7及以後),false(1.6及以前)

1.png
2.png
在jdk1.6及以前,建立c2的時候,會判斷字符串常量池中是否有「asd」字符串對象1,若是有,就直接返回該對象1的引用,若是沒有,會根據c1在堆中建立的字符串對象2在字符串常量池中也建立一個「asd」對象3(此時字符串常量池在永久代方法區中)。而後返回常量池中的對象3的引用。
4.png
3.png
在jdk1.6以後,若是字符串常量池沒有,則直接返回c1在堆中建立的對象2的引用。
特殊狀況:

String e1 = new StringBuffer("ja").append("va").toString();
        String e2 = e1.intern();
        System.out.println(e1==e2);//false

在1.7以後返回false是由於java爲關鍵字,程序在運行到這以前,確定已經加載過java到常量池中。
判斷一共建立了幾個對象

String f1 = new String("a")+new String("sd5");
        String f2 = f1.intern();
        System.out.println(f1==f2);//true(1.7及以後),false(1.6及以前)

6.png
5.png

其餘常量池

Byte,Short,Integer,Long,Character,Boolean都有本身的常量(對象)池

其中Byte,Short,Integer,Long,Character這5種整型的包裝類也只是在對應值在-128與+127(包括)之間才用常量池,由於這些數用到的機率相對較大。

Integer i1 = 127;
        Integer i2 = 127;
        System.out.println(i1 == i2);//true

        Integer i11 = -127;
        Integer i22 = -127;
        System.out.println(i11 == i22);//true

        Integer i3 = 128;
        Integer i4 = 128;
        System.out.println(i3 == i4);//false

        Integer i33 = -128;
        Integer i44 = -128;
        System.out.println(i33 == i44);//true

        Integer i339 = -129;
        Integer i449 = -129;
        System.out.println(i339 == i449);//false

用new關鍵字爲新建對象,因此爲false

Integer aa = new Integer(127);
        Integer bb = new Integer(127);
        System.out.println(aa == bb);//false

boolean使用了常量池

Boolean bool1 = true;
        Boolean bool2 = true;
        System.out.println(bool1 == bool2);//true

double、float類型的包裝類沒有實現對象池技術

Double d11 = 1.0;
        Double d22 = 1.0;
        System.out.println(d11 == d22);//false
Float d13 = 1.0f;
        Float d24 = 1.0f;
        System.out.println(d13 == d24);//false
相關文章
相關標籤/搜索