java 自动装箱与拆箱

2019-03-04  本文已影响0人  Galileo_404

什么是自动装箱和拆箱


自动装箱和拆箱在编译器中实现。
从原始数据类型(字节,短整型,长整型,浮点型,双精度型,字符型和布尔型)到对应包装器对象(字节,整数,长整数,浮点型,双精度,字符和布尔)的自动换行被称为自动装箱(AutoBoxing)。
反向,从包装器对象到其对应的原始数据类型,被称为取消装箱。

实现


装箱主要调用xx.valueOf(),比如Integer.valueOf(int);
自动拆箱,调用类似intValue(),doubleValue(), 等方法。

Integer n  = 2;  // Boxing 
int a  = n;        // Unboxing

反编译后为

Compiled from "AutoBox2.java"
public class AutoBox2 {
  public AutoBox2();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: bipush        8
       2: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       5: astore_1
       6: aload_1
       7: invokevirtual #3                  // Method java/lang/Integer.intValue:()I
      10: istore_2
      11: return
}

自动装箱调用Integer.valueOf()获取包装后的对象,自动拆箱调用Integer.intValue()获取对应基础值。除了Integer,其余基础类型都有对应装箱和拆箱函数。

装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的。(xxx代表对应的基本数据类型)。

实例


public static void main(String[] args) {
        Integer a = 1;
    }

Integer a = 1;
对于java编译器会将该语句编译为Integer a = Integer.valueOf(1);完成自动装箱。

与Integer i = new Integer(1)区别:该方法不会触发自动装箱。

Integer i = null;
int j = i;

上面代码反编译如下

public static void main(String[] args)
  {
    Integer i = null;
    int j = i.intValue();
  }

i对象没有进行初始化或者为Null,在进行拆箱时,在null上执行intValue();会抛出触发NullPointerException错误。

public static void main(String[] args) {
        Integer i1 = 100;
        Integer i2 = 100;
        if (i1 == i2) {
            System.out.println("i1 == i2");
        }
        else {
            System.out.println("i1 != i2");
        }
    }

运行结果:i1 == i2
如果修改 i1,i2值为200,运行结果为i1 != i2
反编译为

public static void main(String[] args)
  {
    Integer i1 = Integer.valueOf(200);
    Integer i2 = Integer.valueOf(200);
    if (i1 == i2) {
      System.out.println("i1 == i2");
    } else {
      System.out.println("i1 != i2");
    }
  }

在赋值是调用Integer.valueOf()进行自动装箱,不过Integer.valueOf()内部,存在缓存对象。
Integer.valueOf()的具体实现:

public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

调用Integer.valueOf()方法,对-128到127的Integer对象进行缓存。如果参数在这个范围,直接返回缓存对象,如果不在新建Integer对象。使用"==",比较对象是,比对的是对象的地址;当参数在-128到127之间,返回缓存中对象,地址相同;如果不在这个范围,新建对象,两个对象内存地址不会相同。

对于Boolean、Byte、Character、Short、Integer、Long六种基本类型,
对于一个字节以内的值-128到127(Boolean只有true和false)都实现了缓存机制。
不在此范围的数才在对应的valueOf()方法中new出一个新的对象。
但是对于Double和Float类型的浮点数据,在-128到127之间除了256个整数外还有无数的小数,
故java中没有实现Double和Float中一些数的缓存。
所以,对于Double和Float的自动装箱,都是new出新的对象

对象进行判断相同时,使用equals,不是使用"=="。

GC压力


public static void main(String[] args) {
        Integer sum = 0;
        for(int i=1000; i<5000; i++){
            sum+=i;
        }
    }

反编译为

public static void main(String[] args)
  {
    Integer sum = Integer.valueOf(0);
    for (int i = 1000; i < 5000; i++) {
      sum = Integer.valueOf(sum.intValue() + i);
    }
  }

sum+=i,+操作不适用Integer对象,sum首先拆箱操作,进行数值相加,累加完成后,在进行自动装箱操作转化为Integer对象;整个循环中会创建将近4000个无用的Integer对象。


使用“==”进行比较时:

参考


上一篇下一篇

猜你喜欢

热点阅读