首页 > 技术文章 > 自动装箱、自动拆箱

hyfer 2019-06-24 21:49 原文

自动装箱:把基本类型用它们对应的引用类型包装起来,使它们具有对象的特质,可以调用toString()、hashCode()、getClass()、equals()等方法。

        如下:

        Integer a=3;//这是自动装箱

        其实编译器调用的是static Integer valueOf(int i)这个方法,valueOf(int i)返回一个表示指定int值的Integer对象,那么就变成这样: 

        Integer a=3;   =>    Integer a=Integer.valueOf(3);

拆箱:跟自动装箱的方向相反,将Integer及Double这样的引用类型的对象重新简化为基本类型的数据。

         如下:

         int i = new Integer(2);//这是拆箱

         编译器内部会调用int intValue()返回该Integer对象的int值

注意:自动装箱和拆箱是由编译器来完成的,编译器会在编译期根据语法决定是否进行装箱和拆箱动作。

集合框架中必须用包装类,不会自动装箱。


问题:自动拆箱和调用构造函数初始化的对象有区别

可以看一下valueOf()的实现

      public static Integer valueOf(int i) {  
          if(i >= -128 &&i <=IntegerCache.high)  
             //如果i在-128~high之间,就直接在缓存中取出i的Integer类型对象
             return IntegerCache.cache[i + 128];  
          else
             return new Integer(i); //否则就在堆内存中创建 
      }  

也就是说,这个东西是有缓存的,同理Char、Long也有缓存(-XX:AutoBoxCacheMax=<size>可以调整)

如果你将两个在缓存区间内的原始值赋给一个包装类型的变量,那么他们的内存地址完全一致。

这时,如果无论你怎么判断等价性,这两者都是等价的。

但是,如果这个原始值超出了缓存范围,或者使用了new Integer(i)这种显式的构造函数,那么这两者的内存地址就是不相同的。

这个不相同,通过equals()和hashcode()是看不出来的,因为他们在包装类里被重写了。

唯一能看出这两个区别的,就是A==B。

Integer a = new Integer(10086);
Integer b = new Integer(10086);
System.out.println(a==b);
//结果:false

Integer a = new Integer(2);
Integer b = new Integer(2);
System.out.println(a==b);
//结果:false

Integer a = 2;
Integer b = 2;
System.out.println(a==b);
//结果:true

Integer a = 114514;
Integer b = 114514;
System.out.println(a==b);
//结果:false

Integer a = 114514;
Integer b = 114514;
System.out.println(a.equals(b));
//结果:true

 

推荐阅读