程序运行过程当中,会把一些常量放在一块区域中,以便下次使用时不用建立之间使用该区域的值,该区域就是常量池,常量池主要用于存放编译期生成的各类字面量和符号引用。常量池在jdk1.6及以前是放在永久代中,即方法区(元空间)中,在1.7中,常量池仍在永久代中,但字符串常量池移到了堆中,在1.8,永久代被移除,常量池就被放在了方法区中。字符串常量池在堆中。java
字符串对象的建立方式有三种:app
String s1 = “asd”;jvm
String s2 = new String(“asd”);优化
String s3 = s1.intern();ui
第一种:spa
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();
会返回一个新的引用。
第二种:
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