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

weblog 精帖
1841

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了两个不同的对象,所以结果一目了然。

分析到此结束。

猜你喜欢
  • blog js中const,var,let

    1.const定义变量不可以修改,而且必须初始化。<script> const b; //报错Uncaught SyntaxError: Missing initializer in const declaration
  • blog java集合之TreeMap实现

    java集合之TreeMap实现         TreeMap集合实现其实说简单也简单说复杂也复杂,说简单是因为TreeMap底层实现完全依靠红黑树这个数据结构,相比与HashMap来说Tr
  • blog jdbc连接oracle数据库两种方式(服务名/SID)

    方式1,使用服务名 jdbc:oracle:thin:@localhost:1521/orclpdb orclpdb是oracle数据库服务名 方式2,使用SID jdbc:oracle:thin:@localhost:152
  • blog 算法-特

    问题描述:思路:遍历1-n个数,判断是否满足条件。代码:package club.test;public class TestMain11 { public static void main(String[] args) { int nu
  • ofc a* 搜索算法实现( a-star )

    a* 搜索算法实现( a-star )
  • blog springboot框架注入分页查询基础数据(定义参数解析器)

            一般分页查询接口都需要传入page(当前第几页),limit(限制行数)两个参数。如果框架本身没有做处话我们需要己在控制层写参数去接收这俩参数。如何配置基础框架,让其
  • ofc mysql修改主键id增初始值

    mysql修改主键id增初始值
  • blog mysql事务四大特性以隔离级

    事务四大特性 一般来说,事务是必须满足4个条件(ACID):子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。子性:一
  • blog linux安时分设置

    Linux分挂载点介绍分类型介绍备注/boot启一般设置100M-200M,boot目录包含了操作系统内核在启系统过程中所要用到文件。/根分所有未指定挂载点目录都会放到这个挂载点下。/home用户目录一般每个用户100
  • blog java态编译技术分析

    1.态编译技术 从 JDK 1.6 开始引入了用 Java 代码重写编译器接口,使得我们可以在运行时编译 Java 源码,然后用类加载器进行加载,让 Java 语言更具灵活性,能够完成许多高级操作。 2.本次要实现功能 态编译