美文网首页
java 自动装箱与拆箱

java 自动装箱与拆箱

作者: Galileo_404 | 来源:发表于2019-03-04 15:36 被阅读0次

    什么是自动装箱和拆箱


    自动装箱和拆箱在编译器中实现。
    从原始数据类型(字节,短整型,长整型,浮点型,双精度型,字符型和布尔型)到对应包装器对象(字节,整数,长整数,浮点型,双精度,字符和布尔)的自动换行被称为自动装箱(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)区别:该方法不会触发自动装箱。

    • 拆箱可能存在NullPointerException异常
    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对象。


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

    • 当基本类型包装类与基本类型值进行==运算时,包装类会自动拆箱。即比较的是基本类型值。
    • 当两边都是包装类型,在==运算时不会触发拆箱操作;所以比较的是引用地址
    • 在一侧存在表达式,会触发拆箱操作,运算后的结果为基本类型;然后包装类型与基本类型进行==运算,触发拆箱操作

    参考


    相关文章

      网友评论

          本文标题:java 自动装箱与拆箱

          本文链接:https://www.haomeiwen.com/subject/veibuqtx.html