浅析Java中的装箱和拆箱

毕业了,辞了成都的工作到深圳重新开始找,笔试时发现蛮多题考装箱拆箱知识的,这里简单总结一下。

什么是装箱和拆箱

要想弄懂这两个概念,首先要明白,Java中有基本类型包装类型之分。
基本类型:int 、char、float 、double等
包装类型:Integer、String、Float、Double等

装箱就是把基本类型转换为对应的包装类型
拆箱就是把包装类型转换为基本类型

public class Test {
    public static void main(String[] args) {        
        Integer a=10;   //装箱
        int b=a;    //拆箱
    }
}

实现原理

我们反编译下上面的代码:
这里写图片描述

2:7:可以看出,Integer这个包装类,装箱和拆箱时分别用了valueOf()intValue()这两个方法。

即上面的代码可以表示为:

public class Test {
    public static void main(String[] args) {        
        Integer a=Integer.valueOf(10);    //装箱
        int b =a.intValue();    //拆箱
    }
}

笔试题

除了Integer,Float、Double等其余包装类也都是使用valueOf()进行装箱,xxxValue()进行拆箱。
接下来,我们来点笔试题,来进一步了解”装箱拆箱”的细节。

1. Integer类型的缓存内部类

public class Test {
    public static void main(String[] args) {        
        Integer a1=100;
        Integer a2=100;
        Integer b1=200;
        Integer b2=200;
        Integer c1=new Integer(100);
        Integer c2=new Integer(100);

        System.out.println(a1==a2);
        System.out.println(b1==b2);
        System.out.println(c1==c2);
    }
}

输出结果是:

true
false
false

c1==c2为false因为内存地址不一样,那a1==a2为true、b1==b2却为false是怎么回事?

这里很容易迷惑,若装箱后生成的Integer类型全为重新new的对象,那比较时应为false,但为何比较100时为true呢?

答案是Integer的缓存机制。类似于String的字符串池一样,为了提高Integer的内存利用率,Integer类中有一个静态内部类IntegerCache。里面已经创建了-128~127的Integer对象。
所以装箱的-128~127的Integer指向的为同一个已经创建好的Integer对象。

我们看看源码就清楚了:
[-128,127]范围内的,直接返回的Integer内部缓存类中的“固定”对象,而超过了则重新创建。

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

接下来是Integer缓存类源码,其实就是Integer的静态内部类:

private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }

        private IntegerCache() {}
    }

2. 运算符对装拆箱的影响

我们再来通过一段代码了解运算符对装拆箱的影响

        Integer a=1;
        Integer b=2;
        Long c=2L;

        Integer d=new Integer(1);
        Integer e=new Integer(2);

        System.out.println(b.equals(c));
        System.out.println(d==e);
        System.out.println(3L==a+b);
        System.out.println(e==1+new Integer(1));

输出结果是:

false
false
true
true

这段代码的结果说明如下几个知识点:

  • 包装类的equals方法只比较同类型的类
    不同的类即使数值相等也会返回false。所以b.equals(c)会输出false。以Long的equals方法源码为例:
  public boolean equals(Object obj) {
        if (obj instanceof Long) {
            return value == ((Long)obj).longValue();
        }
        return false;
    }
  • “+-*/”运算符会触发包装类的拆箱,“==”对于非对象比较数值
    对于3l==a+b,首先a和b会进行拆箱,然后相加,然后提升类型,进行数值比较,结果为true。
    对于d==e,由于比较的是两个对象,所以比较地址,为false。
    对于e==1+new Integer(1),先是右边拆箱,相加,此时由于“==”比较的是数值与对象,所以对象会进行拆箱,最后比较数值,为true。

如果有疑问,可以自己反编译下,结果会很清晰。

3. 其余包装类的装拆箱

再简单补充下其余包装类装拆箱的特点

  1. Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。它们都有对应的[-128,127]的缓存内部类。
  2. Double、Float的valueOf方法的实现是类似的。没有缓存内部类。仔细想一下也会知道为什么,[-128,127]区间内的浮点数数目是无穷的,无法缓存下来提高效率。
  3. Boolean比较特别,它的TRUEFALSE其实是两个final的静态成员变量。所以valueOf方法返回的总是这两个变量。

用代码来验证一下:

        Long a=50L;
        Long b=50L;
        Long aa=500L;
        Long bb=500L;   
        System.out.println(a==b);
        System.out.println(aa==bb);

        System.out.println("---------分割线---------");
        Double c=50.5;
        Double d=50.5;
        Double cc=500.5;
        Double dd=500.5;
        System.out.println(c==d);
        System.out.println(cc==dd);

        System.out.println("---------分割线---------");
        Boolean e=true;
        boolean f=true;
        System.out.println(e==f);

输出结果:

true
false
———分割线———
false
false
———分割线———
true



本文部分内容参照如下文章作的总结:
深入剖析Java中的装箱和拆箱

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值