深入理解jvm-泛型擦除与语法糖(拆箱装箱)

12 篇文章 1 订阅

本文为读书笔记

1. 泛型擦除

擦除式泛型的实现几乎只需要在Javac编译器上做出改进即可,不需要改动字节码、不需要改动Java虚拟机,也保证了以前没有使用泛型的库可以直接运行在Java 5.0之上。

泛型擦除的实现:

擦除前:
在这里插入图片描述

擦除后:
在这里插入图片描述

可见:
java的类型擦除是把T 在编译时直接抹去,编程类似于ArrayList list 这样的裸类型;而在调用的时候进行强制类型转换;

但是问题出现了:
在这里插入图片描述
这样的代码目前java是不支持的,想要用基本类型的集合类,得用包装类,那么问题就出现,每次调用集合类都要进行一次拆箱装箱,这无疑是对性能的一种开销;

2. 自动拆箱装箱

装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型。
在这里插入图片描述
在这里插入图片描述
以及:
在这里插入图片描述
第一个true:
反编译一下看调用了什么:
在这里插入图片描述
执行了 Integer.valueof这个static方法
在这里插入图片描述

 private static class IntegerCache {
        static final int low = -128;
        static final int high;
          static {
        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;
          }
          一般情况下是127

所以可见,返回的是缓存池里池内同一个对象

超过范围:-128-127 之后就不会从缓存池里取了;

故第二个 false 也能解释了;

第三四个true:
在这里插入图片描述
调用的是Integer.intValue来获取值,最后判等就行了故为true

**注意 == 是值相等 **

再来看g 由于 拆箱装箱,所以 左边是一个long的 3 == 右边 int 3
相等

最后一个:
在这里插入图片描述
调用的是long的equals

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

很明显不是 Long类型,故false

由此可以得出结论:

数值类型包装类 装箱(设置值) valueof() 拆箱(获取值) xxxValue()

== 值相等的前提是 左右触发了拆箱 ==做值相等,而不是地址相等
在这里插入图片描述
在这里插入图片描述
equals为重写方法,调用相关基本类型的包装类方法:
首先判断实例类型,再判断值

给大家总结以下 :
+会触发语法糖 反编译就是IntValue,因为CPU只认识int类型
无+就是valueOf其中涉及到Integer的缓存值
Integer.equals Integer重写了方法,如果是同类型比较则比较value值。

3. 堆污染

什么时候会发生堆污染呢?

当参数化类型的变量指向一个非参数化类型的对象时,会发生堆污染。如果程序在执行某些操作,在编译时产生 unchecked warning (未经检查的警告),就会出现这种情况。无论是 编译时 还是 运行时 ,如果无法验证一个涉及参数化的类型的操作(例如:类型强转、方法调用)是否正确,就会产生一个 unchecked warning 。

也就是说,当一个参数化的类型无法再编译或运行时被确定,会产生一个 unchecked warning ,这时,就发生了堆污染。

前面有提到,由于泛型擦除,泛型是不能显示的完成转型、instanceof 和 new 表达式 的操作的。比如下面的例子:

// 语句 1
List list = new ArrayList<Integer>();
// 语句 2 -- unchecked warning 
List<String> strList = list;

由于语句 1 声明了一个无泛型的对象 list(实际存储的是Integer类型的参数),语句 2 将无泛型的 list 对象,赋给了 String 类型 的泛型变量 strList,此处会发生堆污染。为什么呢?

由于泛型擦除,new ArrayList() 和 List strList 会被擦除为 ArrayList 和 List,当 list 指向 strList 时,编译器是无法确定 list 和 strList 的参数类型的,因为在运行时 list 的类型信息 “Integer” 和 strList 的类型信息 “String” 都已经被擦除了,这时就会产生 unchecked warning ,自然就会发生 堆污染了。

在正常情况下,当所有代码同时编译时,编译器会发出未经检查的警告,以引起对潜在堆污染的注意。如果单独编译代码的各个部分,则很难检测到堆污染的潜在风险。如果确保代码在没有警告的情况下编译,则不会发生堆污染。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值