有关Java包装类的自动装箱和拆箱--使用注意事项

事出有因

在最近准备安卓面试的过程中,一位资深的大神问了我关于装箱和拆箱的一些知识点。无奈小弟平时没怎么关注这个方面的知识点,拿来就是用,完全没有没有考虑到使用上的性能损耗和一些注意事项问题。所以经历此面试之后,决定好好复习一下关于这方面的知识点,并总结出这篇文章,供自己日后快速复习,同时也希望本篇文章能给各位看官带来收益。

例子1

· 知识点

1 自动装箱 & 自动拆箱

2 比较符“==”和“equal”,在使用上的注意事项

    public static void main(String[] args) {
        Integer a = 1;
        Integer b = 2;
        Integer c = 3;
        Integer d = 3;
        
        Integer e = 220;
        Integer f = 220;
        Long g = 3L;

        System.out.println(c == d);
        System.out.println(e == f);

        System.out.println(c == (a + b));
        System.out.println(c.equals(a + b));

        System.out.println(g == (a + b));
        System.out.println(g.equals(a + b));
    }
复制代码

小伙伴们先猜想上面程序输出的结果,认真的思考一下。

· 解释

在程序中变量:a,b,c,d,e,f,g都是对基本数据包装类,在初始化这些变量的时候,实际上java编译器帮我们使用装箱来赋值。例如:Integer a = 1; 编译后换算成 Integer a = Integer.valueOf(1); 这个过程就被称为装箱啦,需要注意一点的是变量a为Integer对象类型,为实际保存值的地址引用。

当打印:c == d的时候,此时两者的对象类型都是Integer,那么此时比较的是两者的地址引用。此处的结果返回为true,那么我们能间接得出结论:c和d都指向同一个对象,指向的对象值为3

当打印:e == f的时候,同样的此时两者的对象类型都是Integer,那么此时比较的是两者的地址引用。但是此处返回的结果为false,那么我们能间接得出结论:e和f指向不同的对象,但是对象的值都为220。 对比上面两条结果,我们是否有疑问。为啥第一条打印为true,第二条打印为false。想要搞懂这其中的原因,那么就需要知道Integer中的Cache的概念,下面贴出其中的源码:

    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) {
                try {
                    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);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

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

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }
复制代码

上面的源码不是特别复杂,各位看官可以先大致观摩一下,再来看我的解析,理解起来能够事半功倍。 源码解析:IntegerCache是Integer的静态内部类,用来缓存Integer对象。使用数组的形式来缓存,并且数组的大小为256,缓存值的范围为:[-128 ,127]。如果值的取值范围没有在这个区间中,那么这个对象将不会保存

结论:值为3的Integer对象,会保存在缓存中。值为220,不在保存的区间范围内,所以每次都是创建新的Integer类型对象

当打印“c == (a+b)” 的时候,结果为true。为什么为true?

我来解析一下这个过程: a+b等同于a.value+b.value , 其中a.value = 1,b.value = 2 ,所以:a+b的结果为基本类型3(这个过程就是拆箱的过程,一般在遇到算数运算符的时候,包装类型就是自动拆箱)“c == (a+B)” 就等同于 “c == 3”,因为此时的c的类型为Integer,所以需要拆箱比较,最终的比较形式为:c.value == 3

当打印“c.equals(a+b)”的时候,结果也为true 和上面一样,解析一下过程:a+b的解析和上面一样。我们重点来看"c.equals(a+b)",Integer.equals()方法需要传入Object对象,那么a+b需要装箱变为Integer对象。程序就变成“c.equals(Integer.value(3))”,所以结果为true

注意事项:包装类的装箱和拆箱涉及到性能损耗,因为程序需要多执行几步。涉及到基本算术运算符,就会涉及到拆箱,变量初始化涉及到装箱

当打印“g == (a + b)”,因为(a+b)得到结果为基本类型3。变量g的类型为Long,当执行g == 3的时候需要拆箱变成3 == 3 。所以得到的结果为true。

当打印“g.equals(a + b),结果为false。为什么结果不是true,因为:g的类型为Long,那么Long.equals()方法比较的对象类型也需要为Long,源码如下:

public boolean equals(Object obj) {
        if (obj instanceof Long) {
            return value == ((Long)obj).longValue();
        }
        return false;
    }
复制代码

因为a+b得到的结果为整型3,所以程序变成g.equals(Integer.valueOf(3)),可以看到两者对象的类型都不相同,尽管他们的值都为3,但程序结果还是返回false

注意事项:当使用不同包装类的equals方法时候,需要注意比较的类型不同结果直接返回false

猜你喜欢

转载自juejin.im/post/5e9c44ebf265da47e34c0cac