关于java:5常量池解析

22次阅读

共计 2927 个字符,预计需要花费 8 分钟才能阅读完成。

程序运行过程中,会把一些常量放在一块区域中,以便下次应用时不必创立之间应用该区域的值,该区域就是常量池,常量池次要用于寄存编译期生成的各种字面量和符号援用。常量池在 jdk1.6 及之前是放在永恒代中,即办法区(元空间)中,在 1.7 中,常量池仍在永恒代中,但字符串常量池移到了堆中,在 1.8,永恒代被移除,常量池就被放在了办法区中。字符串常量池在堆中。

字符串常量池

字符串对象的创立形式有三种:

String s1 =“asd”;

String s2 = new String(“asd”);

String s3 = s1.intern();

第一种:

        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”;

这种形式,jvm 会间接在堆中的常量池中创立一个“asd”字符串对象(前提是原来没有),而后返回该对象的援用,如果下次有应用该字符串,则不必创立,间接返回该援用。

状况如下:

String a2 =“asd”;a2 返回的与 a1 是同一个对象援用,a1 与 a2 是相等的。

String a3=“a”+”s”+”d”; 程序在运行过程中在动态编译阶段,虚拟机就会对代码进行优化,对于那些能够确定的变量值会间接优化应用,也就是等价于 a3=“asd”;所以是与 a1 和 a2 相等的,。

String a4 =“a”;

String a5 = a4+“sd”,这时,a5=“asd”,然而 a5 与上述 a1、a2、a3 不相等,因为 a5 在创立的时候,存在 a4 的援用,而援用的值在编译期是无奈确定的,只有在程序运行期来动态分配并将连贯后的新地址赋给 a5。所以无奈进行优化,即在字符串常量池中又产生了一条新的字符串。其运行原理如下

StringBuilder temp = new StringBuilder();

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

String a5 = temp.toString();

会返回一个新的援用。

第二种:

        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 及之前)



在 jdk1.6 及之前,创立 c2 的时候,会判断字符串常量池中是否有“asd”字符串对象 1,如果有,就间接返回该对象 1 的援用,如果没有,会依据 c1 在堆中创立的字符串对象 2 在字符串常量池中也创立一个“asd”对象 3(此时字符串常量池在永恒代办法区中)。而后返回常量池中的对象 3 的援用。


在 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 及之前)


其余常量池

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

正文完
 0