文章演示了在 Java 中获取 Class 对象的多种方式:①通过实例调用 getClass();②使用 Class.forName(全限定名);③直接使用类名.class;④基本类型包装类的 TYPE 字段获取原始类型 Class;⑤通过已有 Class 的 getSuperclass() 获得父类 Class。示例代码展示了 Student 与其父类 Person 的定义,以及每种方式的运行结果,说明不同获取方式返回的 Class 对象在哈希码上可能相同或不同,且 TYPE 属于 JDK 内部实现。

本文梳理JVM主要垃圾收集器及适用场景。Serial 单线程,适合Client新生代;ParNew 多线程版,可配CMS;Parallel Scavenge 与 Parallel Old 并行复制+标记-整理,侧重吞吐量;Serial Old 单线程老年代,作Client或CMS 备用;CMS 并发标记-清除,追最短停顿但易碎片;G1 分区收集实现可预测暂停,调参简便。概述各收集器优缺点及关键参数。

桥接方法是JDK 1.5引入泛型后,为了保持与旧版本字节码的兼容性,由编译器自动生成的方法。可以通过`Method.isBridge()`判断一个方法是否为桥接方法,其字节码会被标记为`ACC_BRIDGE`和`ACC_SYNTHETIC`。 桥接方法主要在以下几种情况产生:1) 实现泛型接口(如`Consumer<T>`、`Supplier<T>`、`Function<T,R>`);2) 子类覆盖超类方法并升级返回类型;3) 静态方法不会生成桥接方法;4) 升级访问修饰符不会生成桥接方法。 生成桥接方法的核心原因是Java泛型的类型擦除机制。为了在编译时移除泛型信息,保证与旧版本代码的兼容性,编译器会生成桥接方法来处理类型转换和方法调用,尽管运行时实际执行的是原始类型的方法,但桥接方法在类型检查时可能抛出异常。总而言之,桥接方法的存在是为了保证泛型代码在不同Java版本之间的兼容性。

Java 泛型在编译后会被“类型擦除”,即所有泛型信息在进入 JVM 前被去除,生成的类与普通类无区别。擦除时,未限定的类型参数变为 `Object`,有上限的则替换为上限类型(如 `T extends CharSequence` 变为 `CharSequence`)。因此不同泛型实例的 `Class` 相同,字段实际类型均为擦除后的类型。由于擦除,编译期的类型安全检查可以被反射绕过,例如通过反射调用 `ArrayList.add(Object)` 向 `ArrayList<Integer>` 中加入字符串,运行时仍能成功。整体说明了 Java 泛型的实现机制及其“伪泛型”特性。

文章通过示例说明 Java 泛型中 ? extends 与 ? super 的区别,提出 PECS(Producer Extends, Consumer Super)法则:读取数据使用 extends,写入数据使用 super;若需兼读写则不使用通配符。代码演示了在 List<? extends Fruit> 中不能添加元素但可以安全读取,在 List<? super Fruit> 中只能安全写入且读取返回 Object。文中还比较了 Kotlin 的 out/in 关键字对应协变、逆变,并指出数组在 Java 中是协变的。最后介绍了 Kotlin 的泛型实化(reified)及 inline 实现原理,归纳了 PECS 的使用场景。

类型通配符用于统一表示不同泛型 List 的父类,分为三类:①无界通配符 `<?>`,表示元素类型未知的 List,不能向其中写入元素;②上界通配符 `<? extends T>`,表示 List 中元素类型为 T 或其子类;③下界通配符 `<? super T>`,表示 List 中元素类型为 T 或其父类。文章通过代码示例演示了这三种通配符在 List 声明中的合法写法及不合法情况,帮助读者理解其作用与使用限制。