Java 是一种动态连接语言,在Java程序中,有很多东西不会在运行过程中变化。而这些在JVM解释执行程序的时候是非常重要的。那么编译器将源程序编译成class文件后,会用一部分字节分类存储这些代码。而这些字节我们就称为常量池。
java中基本类型的包装类的大部分都实现了常量池技术,这些类是Byte,Short,Integer,Long, Character,Boolean,另外两种浮点数类型的包装类则没有实现。
Byte,Short,Integer,Long,Character这5种整型的包装类也只是在对应值小于等于127时才可使用对象池,也即对象不负责创建和管理大于127的这些类的对象。
//5种整形的包装类Byte,Short,Integer,Long,Character的对象,
//在值小于127时可以使用常量池
Integer i1=127;
Integer i2=127;
System.out.println(i1==i2)//输出true
//值大于127时,不会从常量池中取对象
Integer i3=128;
Integer i4=128;
System.out.println(i3==i4)//输出false
//Boolean类也实现了常量池技术
Boolean bool1=true;
Boolean bool2=true;
System.out.println(bool1==bool2);//输出true
//浮点类型的包装类没有实现常量池技术
Double d1=1.0;
Double d2=1.0;
System.out.println(d1==d2)//输出false
两个new Integer(),即使值相等,生成的是两个对象,地址不同,因此==判断为不相等。
两个Integer变量如果值在-128至127之间,则地址都在常量池中,==判断为相等;如果超过这个范围,则不相等。
将int数值直接赋给Integer变量时,Integer变量会调用Integer.valueOf()方法包装起来,而valueOf方法仍然会优先考虑使用常量池。
Integer变量和new Integer(),即使值相等,地址不同,==判断为不相等。
int与Integer、new Integer()比较,后两者会自动拆箱为int类型,因此只要值相等,==判断为相等。
如果Integer变量之间存在运算,则自动拆箱,之后再计算出结果,最后包装,使用Integer.valueOf()方法。
int i = 128;
Integer i2 = 128;
Integer i3 = new Integer(128);
System.out.println("i == i2 = " + (i == i2)); // Integer会自动拆箱为int,所以为true
System.out.println("i == i3 = " + (i == i3)); // true,理由同上
Integer i4 = 127;// 编译时被翻译成:Integer i4 = Integer.valueOf(127);
Integer i5 = 127;
System.out.println("i4 == i5 = " + (i4 == i5));// true
Integer i6 = 128;
Integer i7 = 128;
System.out.println("i6 == i7 = " + (i6 == i7));// false
Integer i8 = new Integer(127);
System.out.println("i5 == i8 = " + (i5 == i8)); // false
Integer i9 = new Integer(128);
Integer i10 = new Integer(128);
System.out.println("i9 == i10 = " + (i9 == i10)); // false
Integer a = new Integer(127), b = new Integer(128);
int c = 127, d = 128, dd = 128;
Integer e = 127, ee = 127, f = 128, ff = 128;
System.out.println(a == b); // false 因为a,b都是new出来的对象,地址不同所以为false
System.out.println(a == c); // true a会自动拆箱为int类型
System.out.println(a == e); // false 指向的地址不同a是new出来的
System.out.println(e == c); // true e会自动拆箱为int类型
System.out.println(e == ee);// true Integer对 处于-128到127范围之间,指向了同一片地址区域
System.out.println(b == f); // false 指向的地址不同b是new出来的
System.out.println(f == d); // true f自动拆箱为int类型
System.out.println(f == ff); /*
* false 指向的不是同一片地址区域。
* 在Integer类型中,-128到127存放的是同一片区域地址,
* 之外的数是另外开辟空间来进行 存储的
*/
System.out.println(d == dd); // true 不解释
Integer i01 = 59;
int i02 = 59;
Integer i03 =Integer.valueOf(59);
Integer i04 = new Integer(59);
以下输出结果为false的是:
System.out.println(i01== i02);//true
System.out.println(i01== i03);//true
System.out.println(i03== i04);//false
System.out.println(i02== i04);//true
Integer i01 = 128;
int i02 = 128;
Integer i03 = Integer.valueOf(128);
Integer i04 = new Integer(128);
以下输出结果为false的是:
System.out.println(i01 == i02);//true
System.out.println(i01 == i03);//false
System.out.println(i03 == i04);//false
System.out.println(i02 == i04);//true
源代码中所有相同字面值的字符串常量只可能建立唯一 一个拘留字符串对象。可以调用String的intern()方法来使得一个常规字符串对象成为拘留字符串对象。
String s=new String(“Hello world”); 编译成class文件后的指令:事实上,在运行这段指令之前,JVM就已经为"Hello world"在堆中创建了一个拘留字符串( 值得注意的是:如果源程序中还有一个"Hello world"字符串常量,那么他们都对应了同一个堆中的拘留字符串)。然后用这个拘留字符串的值来初始化堆中用new指令创建出来的新的String对象,局部变量s实际上存储的是new出来的堆对象地址。
String s=“Hello world”:这跟(1)中创建指令有很大的不同,此时局部变量s存储的是早已创建好的拘留字符串的堆地址。
//s1,s2分别位于堆中不同空间
String s1=new String("hello");
String s2=new String("hello");
System.out.println(s1==s2);//输出false
//s3,s4位于池中同一空间
String s3="hello";
String s4="hello";
System.out.println(s3==s4);//输出true
用new String()创建的字符串不是常量,不能在编译期就确定,所以new String()创建的字符串不放入常量池中,他们有自己的地址空间。
String 对象(内存)的不变性机制会使修改String字符串时,产生大量的对象,因为每次改变字符串,都会生成一个新的String。 java 为了更有效的使用内存,常量池在编译期遇见String 字符串时,它会检查该池内是否已经存在相同的String 字符串,如果找到,就把新变量的引用指向现有的字符串对象,不创建任何新的String 常量对象,没找到再创建新的。所以对一个字符串对象的任何修改,都会产生一个新的字符串对象,原来的依然存在,等待垃圾回收。
采用“+”运算符拼接字符串时:
String a = "a1";
String b = "a" + 1;
System.out.println((a == b)); //result = true
String a = "atrue";
String b = "a" + "true";
System.out.println((a == b)); //result = true
String a = "a3.4";
String b = "a" + 3.4;
System.out.println((a == b)); //result = true
String a = "ab";
String bb = "b";
String b = "a" + bb;
System.out.println((a == b)); //result = false
String a = "ab";
final String bb = "b";
String b = "a" + bb;
System.out.println((a == b)); //result = true
String a = "ab";
final String bb = getBB();
String b = "a" + bb;
System.out.println((a == b)); //result = false
private static String getBB() {
return "b";
}
String的intern()方法会查找在常量池中是否存在一份equal方法相等的字符串,如果有则返回一个引用,没有则添加自己的字符串进入常量池,注意:只是字符串部分。所以这时会存在2份拷贝,常量池的部分被String类私有并管理,现存对象拷贝则按对象生命周期继续使用。
对于任何两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern() 才为 true。
class Test
{
private static String a = "ab";
public static void main(String[] args)
{
String s1 = "a";
String s2 = "b";
String s = s1 + s2;
System.out.println(s == a);//false
System.out.println(s.intern() == a);//true
}
}