Java泛型在编译期被擦除,运行时无法获取真实类型参数——这导致instanceof List<String>编译失败、getClass()返回原始类型、不能new T[]或new T()、静态成员不可用泛型参数、基本类型不可作实参,所有限制均源于擦除后JVM仅见Object或上界类型。

直接练核心场景,不堆概念。泛型面试真正卡人的,从来不是“什么是泛型”,而是你写不出、改不对、解释不清的那几类代码。
盯住三类高频真题动手写
别背定义,拿笔写——每类至少手敲 2 遍,边写边自问“这里为什么不能这么写”:
-
擦除导致的运行时失效题:写一个方法,传入
List<String>,尝试用instanceof List<String>判断,编译报错;再改成instanceof List,能过。接着打印list.getClass() == ArrayList.class,结果是true。这个过程必须亲手跑一遍。 -
泛型数组创建失败题:写
T[] arr = new T[5];,IDE 立刻红标;改成T[] arr = (T[]) new Object[5];,能编译但有警告。再补一句arr[0] = (T) "hello";,观察是否报错——这就是擦除后类型信息丢失的实感。 -
通配符边界混淆题:定义方法
void copy(List<? extends Number> src, List<Number> dst),试着往dstadd(1.5f),成功;但往srcadd(1),编译失败。换用? super Integer,反过来验证读写限制。
重点练“不能做什么”背后的逻辑
面试官爱问“为什么不能……”,答案不在语法手册里,在擦除机制里:
- 静态字段/方法不能用
T→ 因为擦除后所有泛型实例共享同一份字节码,static属于类,而T属于实例,二者生命周期冲突; - 不能
new T()→ 擦除后只剩Object,编译器无法确认构造函数是否存在; - 基本类型不能当泛型实参 →
int不是Object子类,而泛型底层全按引用类型处理,所以必须用Integer; -
getClass()拿不到泛型信息 → 运行时只剩原始类型,new ArrayList<String>().getClass()和new ArrayList<Integer>().getClass()完全一样。
用真实接口倒推泛型设计
打开 JDK 源码(如 java.util.List<E>、java.util.Comparator<T>),对照着写实现:
立即学习“Java免费学习笔记(深入)”;
- 自己写一个
SimpleStack<T>,含push(T)、pop()、isEmpty(),注意泛型字段声明和返回值类型; - 再写一个
MaxFinder<T extends Comparable<T>>,实现findMax(T[]),体会上界限定怎么约束类型行为; - 最后写个工具方法
<K, V> Map<K, V> of(K k, V v),理解方法级泛型如何独立于类泛型工作。
讲清楚一道题比背十道题有用
选一道你写过的题(比如“为什么 List<String> 不是 List<Object> 的子类”),用两句话说清:
- Java 泛型是不变的(invariant),不是协变的;
- 如果允许,就能把
Integer放进本该只存String的容器,破坏类型安全——擦除虽在编译后抹掉类型,但编译期必须靠这套规则守住底线。


















