int和Integer的区别及自动装箱拆箱原理

weblog 4683 0 0

int和Integer的区别

说起int和Integer的区别大家耳熟能详的是:

  1. int是java中的基本数据类型,而Integer是引用类型。
  2. Integer必须实例化后才能使用,而int不需要。
  3. 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了两个不同的对象,所以结果一目了然。

分析到此结束。


猜你喜欢
javascript,前端 1142 jsjavascriptforinforof,forof遍历 一、 forin是ES5语法,forof是ES6语法forin是无序遍历数组或对象,也就是随机遍历,不按照顺序来
ASM,java基础 1255   关于cglib代概念常用api,请参考:初步探究cglib态代:http://www.jiajiajia.club/blog/artical/yjw520
算法基础 1492 正向代反向代总体来说正向代反向代在于代对象不一样,正向代对象是客户端,反向代对象是服务端。正向代:客户端一代一服务端反向代:客户端一代一服务端以租
vue 860 true时,都会占据页面位置 二、v-show与v-if 控制手段不同编译过程不同编译条件不同 控制手段:v-show隐藏则是为该元素添加css—display:none,dom元素依旧还在。v-
java基础 3443 初步探究cglib态代之前我们说了一下jdk态代http://www.jiajiajia.club/weblog/blog/artical/60本章说一下cglib态代,做个
weblog 1294 根本:进程是操作系统资源分配基本单位,而线程是处器任务调度执行基本单位。包含关系:一个进程内可以有多个线程(默认有一个主线程),线程是进程一部分,必须依赖于进程而存在,不能独立存在。资源共享:进程之间是不共享资源,多个线程之间是共享资源,所以存在资源竞争问题。
weblog 4120 String类实现其不可变性 对于String类实现从源码中可以看出,String类底层维护着一个final修饰char数组,用来储存字符。并且除了hash这个属性其它属性都声明为
算法基础 12553 ,其实其中M是一个容量单位。比如常用迅雷,电脑管家上显示下载速度都是这个意思。2.换算不同传输速度换算进位是1000,1k=1000bit,1M=1000k,1G=1000M容量单位换算进位
目录
没有一个冬天不可逾越,没有一个春天不会来临。最慢的步伐不是跬步,而是徘徊,最快的脚步不是冲刺,而是坚持。