美文网首页
Java的拆箱和装箱

Java的拆箱和装箱

作者: 34sir | 来源:发表于2018-02-24 11:32 被阅读14次

    概念

    自动装箱与拆箱是 Java 1.5 引入的新特性,是一种语法糖。
    在此之前,我们要创建一个值为 10 的 Integer 对象,只能写作:
    Integer value = new Integer(10);
    而现在,我们可以更方便地写为:
    Integer value = 10;

    自动装箱:从基本数据类型值到其对应的包装类对象的自动转换
    比如 Integer value = 10;,是通过调用 Integer.valueOf 方法实现转换的
    自动拆箱:从包装类对象到其对应的基本数据类型值的自动转换
    比如 int primitive = value;是通过调用 Integer.intValue 方法实现转换的

    发生时机

    • 赋值时

    • 比较时

    • 算术运算时

    • 方法调用时

    补充说明
    关于比较:
    除 == 以外,包装类对象与基本数据类型值的比较,包装类对象与包装类对象之间的比较,都是自动拆箱后对基本数据类型值进行比较,此时就要注意可能引发的空指针问题

    public class Test {
        public static long test(long value) {
            return value;
        }
    
        public static void main(String[] args) {
            Long value = null;
            // ...
            test(value);
        }}
    

    main 方法里的代码实际上相当于调用 test(null);,为什么不直接这样写呢?因为编译不过,会报 错误: 不兼容的类型: <空值>无法转换为long
    由上文可知代码中test(value)需要拆包,那就等同于:long value=value.longValue() ;,value是空自然会抛出空指针异常

    == 比较特殊,因为可以用于判断左右是否为同一对象,所以两个包装类对象之间 ==,会用于判断是否为同一对象,而不会进行自动拆箱操作;包装类对象与基本数据类型值之间 ==,会自动拆箱

    Integer v1 = new Integer(10);
    Integer v2 = new Integer(20);
    if (v1 < v2) { // 自动拆箱
        // ...}if (v1 == v2) { // 不拆箱
        // ...}if (v1 == 10) { // 自动拆箱
        // ...}
    

    缓存

    Java 为整型值包装类 ByteCharacterShortIntegerLong设置了缓存,用于存储一定范围内的值,详细如下:

    类型 缓存值
    Byte -128 ~ 127
    Character 0 ~ 127
    Short -128 ~ 127
    Integer -128 ~ 127(可配置)
    Long -128 ~ 127

    一些情况下,如自动装箱时,如果值在缓存值范围内,将不再创建新对象,直接从缓存里取出返回对象

    Integer v1 = 10;
    Integer v2 = 10;
    Integer v3 = new Integer(10);
    Integer v4 = 128;
    Integer v5 = 128;
    Integer v6 = Integer.valueOf(10);
    System.out.println(v1 == v2); 
    // trueSystem.out.println(v1 == v3); 
    // falseSystem.out.println(v4 == v5); 
    // falseSystem.out.println(v1 == v6); // true
    

    缓存实现机制:享元模式
    Short 类实现源码为例:

    // ...public final class Short extends Number implements Comparable<Short> {
        // ...
        private static class ShortCache {
            private ShortCache(){}
    
            static final Short cache[] = new Short[-(-128) + 127 + 1];
    
            static {
                for(int i = 0; i < cache.length; i++)
                    cache[i] = new Short((short)(i - 128));
            }
        }
    
        // ...
        public static Short valueOf(short s) {
            final int offset = 128;
            int sAsInt = s;
            if (sAsInt >= -128 && sAsInt <= 127) { // must cache
                return ShortCache.cache[sAsInt + offset];
            }
            return new Short(s);
        }
        // ...}
    

    在第一次调用到 Short.valueOf(short) 方法时,将创建 -128 ~ 127 对应的 256 个对象缓存到堆内存里

    这种设计,在频繁用到这个范围内的值的时候效率较高,可以避免重复创建和回收对象,否则有可能闲置较多对象在内存中

    使用不当的情况

    • 性能损耗
    Integer sum = 0;
    for (int i = 1000; i < 5000; i++) {
        // 1. 先对 sum 进行自动拆箱
        // 2. 加法
        // 3. 自动装箱赋值给 sum,无法命中缓存,会 new Integer(int)
        sum = sum + i;
    }
    

    在循环过程中会分别调用 4000 次 Integer.intValue()Integer.valueOf(int),并 new 4000 个 Integer对象,而这些操作将 sum 的类型改为 int 即可避免,节约运行时间和空间,提升性能

    • 空指针
      上文中已经提到过一种空指针的情况,下面介绍一个更为隐蔽的情况:
     public static void main(String[] args) {
            long value = true ? null : 1; // NullPointerException   实际上还是对一个值为 null 的 Long 类型进行自动拆箱
        }
    

    相关文章

      网友评论

          本文标题:Java的拆箱和装箱

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