今天做了一个测试 代码如下
package com.hydee.yss.inint.main;
public class IntegerTest {
public static void main(String[] args) {
IntegerTest i = new IntegerTest();
i.test();
}
public void test() {
int s1 = 40;
int s2 = 40;
int s3 = 0;
Integer s4 = 40;
Integer s5 = 40;
Integer s6 = 0;
Integer s7 = new Integer(40);
Integer s8 = new Integer(40);
Integer s9 = new Integer(0);
Integer s10 = 128;
Integer s11 = 128;
if(s1 == s2 ) {
System.out.println("相等1");
} else {
System.out.println("不想等1");
}
if(s1 == (s2 + s3)) {
System.out.println("相等2");
} else {
System.out.println("不想等2");
}
if(s4 == s5 ) {
System.out.println("相等3");
} else {
System.out.println("不想等3");
}
if(s4 == (s5 + s6)) {
System.out.println("相等4");
} else {
System.out.println("不想等4");
}
if(s7 == s8 ) {
System.out.println("相等5");
} else {
System.out.println("不想等5");
}
if(s7 == (s8 + s9)) {
System.out.println("相等6");
} else {
System.out.println("不想等6");
}
if(s10 ==s11) {
System.out.println("相等7");
} else {
System.out.println("不想等7");
}
}
}
其运行结果如下
相等1
相等2
相等3
相等4
不想等5
相等6
不想等7
看到这个结果,我相信没有接触过Java常量池的同学都会很疑惑,为什么会是这样子的呢?下面我们一个一个来说:
首先前面两个结果我相信大家都是知道的,因为int这个基本类型的变量名和值都会存放在java虚拟机栈中,无论创建多少个变量名不一样但是值一样的基本类型的变量,其在栈中的地址都是一样。
那么我们说到剩下的几个类型的时候,不仅要理解堆和栈区别,还要清楚Java的八种基本类型的包装类(Packaging Type)也有对象池机制。
Integer i4=40;Java在编译的时候会执行将代码封装成Integer i4=Integer.valueOf(40);通过查看Source Code发现:
Integer.valueOf()中有个内部类IntegerCache(类似于一个常量数组,也叫对象池),它维护了一个Integer数组cache,长度为(128+127+1)=256,Integer类中还有一个Static Block(静态块)。
从这个静态块可以看出,Integer已经默认创建了数值【-128-127】的Integer缓存数据。所以使用Integer i4=40时,JVM会直接在该在对象池找到该值的引用。也就是说这种方式声明一个Integer
对象时,JVM首先会在Integer对象的缓存池中查找有木有值为40的对象,如果有直接返回该对象的引用;如果没有,则使用New keyword创建一个对象,并返回该对象的引用地址。因为Java中【==】
比较的是两个对象是否是同一个引用(即比较内存地址),i4和i5都是引用的同一个对象,So i4==i5结果为” 相等“;而使用new方式创建的i7=new Integer(40)、i8=new Integer(40),虽然他们的
值相等,但是每次都会重新Create新的Integer对象,不会被放入到对象池中,所以他们不是同一个引用,输出不想等。同样s10和s11因为其范围已经超出Integer常量池的范围,所以每次都是重新创建
了一个引用放在栈中,所以他们也是不一样的。
看到这里很多人会有疑问,那么4和6为啥是相等而不是不想等呢,其实这里面还涉及到另外一个操作,就是Java的拆箱和装箱的概念,因为Java中的数学计算全部是在栈内存中进行操作,所以会把
他们换成基本类型计算,所以我们看到他们才是相等的。