int和Integer的区别及自动装箱拆箱原理
int和Integer的区别
说起int和Integer的区别大家耳熟能详的是:
- int是java中的基本数据类型,而Integer是引用类型。
- Integer必须实例化后才能使用,而int不需要。
- int的默认值是0,而Integer的默认值是null。
然而仅仅知道这些,对其两者的了解还是远远不够的。那么下面就继续探索。
语法糖的味道-自动装箱和拆箱的原理
什么是自动装箱和自动拆箱呢?定义:Java中基础数据类型与它们的包装类进行运算时,编译器会自动帮我们进行转换,转换过程对程序员是透明的,这就是装箱和拆箱,装箱和拆箱可以让我们的代码更简洁易懂。
换句话说,假如我们代码中定义了一个变量
Integer i=100;
虽然我们代码中是这么写的,但是经过java编译器编译以后它的含义就变了,就会变成下边这样
Integer i=Integer.valueOf(100);
不信的话写行代码验证一下,如下代码:
public class Test{
public static void main(String[] args){
Integer i=200;
}
}
经过反编译以后:
Compiled from "Test.java"
public class Test {
public Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: sipush 200
3: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
6: astore_1
7: return
}
看反编译出来的字节码中有这么一行:
3: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
它的含义就是在调用Integer.valueOf()方法。
以上就是自动装箱的原理。自动拆箱的原理其实和装箱差不多,自动拆箱调用的是Integer.intValue()方法。例如如下方法:
public void test(Integer i){
int j=i;
}
反编译过后
public void test(java.lang.Integer);
Code:
0: aload_1
1: invokevirtual #2 // Method java/lang/Integer.intValue:()I
4: istore_2
5: return
valueOf()方法中的坑
我们来看源码:
/**
* This method will always cache values in the range -128 to 127,
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
看了源码才知道,原来java中对于Integer类型并且是定义在-128到127之间的数会进行缓存。这就解释了我们下边要讨论的问题。
讨论
Integer i=new Integer(1);
Integer j=new Integer(1);
System.out.println(i==j);//false
无容置疑,上面的代码一定会打印false,因为对于两个新new的对象而言地址是永远不能相等的。
Integer i=new Integer(1);
Integer j=1;
System.out.println(i==j);//false
没错,上面的代码还会返回false,因为i变量是new出来的,在对内存中存在,而 j 变量根据我们之前说的,会自动装箱,而且-128到127之间的数值会缓存在常量池中,所以两者地址是不相同的。
Integer i=new Integer(1);
int j=1;
System.out.println(i==j);//true
但是把Integer变成int后,如上边代码,就会返回true了,因为Integer类型的变量在和int类型的变量比较时,Integer类型的变量会先自动拆箱成int,再做比较,所以就是两个int类型的变量比较,当然会返回true了。不相信的话就反编译字节码
public void test();
Code:
0: iconst_1
1: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
4: astore_1
5: iconst_1
6: istore_2
7: aload_1
8: invokevirtual #3 // Method java/lang/Integer.intValue:()I
11: iload_2
12: if_icmpne 19
15: iconst_1
16: goto 20
19: iconst_0
20: istore_3
21: return
看第8行字节码就知道了。
Integer i=127;
Integer j=127;
Integer a=128;
Integer b=128;
System.out.println(i==j);//true
System.out.println(a==b);//false
上边的代码就有点意思了,不过也很简单。说会自动装箱时的缓存(-128到127之间的数值会缓存在常量池中),i和j会缓存再常量池中的同一个对象,而a和b时在堆中new了两个不同的对象,所以结果一目了然。
分析到此结束。