【Java基础】泛型:泛型擦除、通配符、上下界限定(附《思维导图》+《面试高频考点清单》)

简介: 本文系统梳理了Java泛型的核心知识体系,主要内容包括: 泛型概述:介绍了泛型的定义、本质和三大优势(类型安全、代码复用、可读性),以及泛型类、接口和方法的三种使用形式。 泛型擦除:深入解析了Java泛型实现的核心机制,包括擦除规则(无界类型擦除为Object,有界类型擦除为第一个边界类型)、擦除带来的问题(如无法使用instanceof、创建泛型数组等)及其解决方案。 泛型通配符:详细讲解了三种通配符类型(无界通配符、上界通配符和下界通配符)的语法、语义和使用场景。

image.png

Java基础:泛型核心知识体系(泛型擦除、通配符、上下界限定)

一、泛型概述

1.1 定义与本质

泛型(Generics)是JDK 5.0引入的参数化类型特性,允许在定义类、接口、方法时使用类型参数(Type Parameter),这些类型参数在使用时才被指定为具体类型。

本质:类型安全的"模板机制",将数据类型作为参数传递,实现代码复用与类型安全的统一。

1.2 核心优势

  • 类型安全:编译期类型检查,避免运行时ClassCastException
  • 代码复用:一套逻辑可处理多种数据类型
  • 可读性:代码意图更清晰,无需频繁强制类型转换
  • 解耦:数据类型与业务逻辑分离

1.3 泛型的三种使用形式

使用形式 语法示例 说明
泛型类 class Box<T> { ... } 类上定义类型参数
泛型接口 interface List<E> { ... } 接口上定义类型参数
泛型方法 <T> T getValue(Box<T> box) { ... } 方法上独立定义类型参数

二、泛型擦除(Type Erasure)

2.1 核心原理

泛型擦除是Java泛型实现的基石:泛型信息仅存在于编译期,编译完成后所有泛型类型参数都会被擦除,替换为它们的限定类型(无界则为Object)。

设计目的:向后兼容JDK 1.4及之前的版本,保证泛型代码能在旧版JVM上运行。

2.2 擦除规则

  1. 无界类型参数:擦除为Object

    // 编译前
    class Box<T> {
         
        private T value;
        public T getValue() {
          return value; }
    }
    
    // 编译后(擦除结果)
    class Box {
         
        private Object value;
        public Object getValue() {
          return value; }
    }
    
  2. 有界类型参数:擦除为第一个边界类型

    // 编译前
    class NumberBox<T extends Number> {
         
        private T value;
    }
    
    // 编译后
    class NumberBox {
         
        private Number value;
    }
    
  3. 多边界类型参数:擦除为第一个边界类型

    // 编译前
    class ComparableBox<T extends Number & Comparable<T>> {
         
        private T value;
    }
    
    // 编译后
    class ComparableBox {
         
        private Number value;
    }
    
  4. 泛型方法:同样遵循上述擦除规则

    // 编译前
    public static <T extends Comparable<T>> int compare(T a, T b) {
         
        return a.compareTo(b);
    }
    
    // 编译后
    public static int compare(Comparable a, Comparable b) {
         
        return a.compareTo(b);
    }
    

2.3 擦除带来的问题与解决方案

问题1:无法使用instanceof判断泛型类型

// 编译错误!泛型类型被擦除,运行时无法获取
if (obj instanceof List<String>) {
    ... }

// 只能判断原始类型
if (obj instanceof List) {
    ... }

解决方案:使用Class对象作为类型令牌(Type Token)

public <T> boolean isInstance(Object obj, Class<T> clazz) {
   
    return clazz.isInstance(obj);
}

问题2:无法创建泛型数组

// 编译错误!泛型类型被擦除,无法保证数组类型安全
List<String>[] lists = new List<String>[10];

// 允许创建原始类型数组,但不安全
List[] lists = new List[10];

解决方案:使用ArrayList代替泛型数组,或使用反射创建

// 推荐方案
List<List<String>> lists = new ArrayList<>();

// 反射方案(谨慎使用)
List<String>[] lists = (List<String>[]) Array.newInstance(List.class, 10);

问题3:泛型类型参数不能是基本数据类型

// 编译错误!基本类型不能作为泛型参数
List<int> list = new ArrayList<>();

// 必须使用包装类
List<Integer> list = new ArrayList<>();

原因:擦除后类型变为Object,而基本类型不能赋值给Object变量。

问题4:泛型类的静态变量与静态方法不能使用类型参数

class Box<T> {
   
    // 编译错误!静态变量属于类,所有实例共享
    private static T value;

    // 编译错误!静态方法不能使用类的类型参数
    public static T getValue() {
    ... }

    // 正确!泛型方法可以有自己的类型参数
    public static <E> E getElement(E element) {
    ... }
}

问题5:桥接方法(Bridge Method)的生成

当泛型类实现泛型接口或继承泛型父类时,编译器会自动生成桥接方法以保证多态性。

// 泛型接口
interface Comparable<T> {
   
    int compareTo(T o);
}

// 实现类
class MyInteger implements Comparable<MyInteger> {
   
    @Override
    public int compareTo(MyInteger o) {
   
        return 0;
    }
}

// 编译后生成的桥接方法
class MyInteger implements Comparable {
   
    public int compareTo(MyInteger o) {
    ... }

    // 桥接方法,保证多态
    public int compareTo(Object o) {
   
        return compareTo((MyInteger) o);
    }
}

2.4 泛型擦除的本质

Java泛型是"伪泛型",运行时JVM并不知道泛型的存在,所有泛型操作都在编译期完成。这与C++的模板(编译期生成不同类型的类)有本质区别。

三、泛型通配符(Wildcard)

3.1 为什么需要通配符?

泛型不支持协变(Covariance)和逆变(Contravariance),即List<String>不是List<Object>的子类。

// 编译错误!泛型不协变
List<Object> list = new ArrayList<String>();

通配符?的引入就是为了解决泛型的灵活性问题,允许在不破坏类型安全的前提下,实现泛型类型的多态。

3.2 三种通配符类型

1. 无界通配符(Unbounded Wildcard):?

  • 语法:List<?>
  • 含义:表示任意类型的泛型
  • 适用场景:当方法不依赖于泛型的具体类型时
// 打印任意类型的List
public static void printList(List<?> list) {
   
    for (Object obj : list) {
   
        System.out.println(obj);
    }
}

// 可以传入任何类型的List
printList(new ArrayList<String>());
printList(new ArrayList<Integer>());

注意List<?>不能添加任何元素(除了null),因为编译器无法确定具体类型。

List<?> list = new ArrayList<String>();
list.add(null); // 允许
list.add("hello"); // 编译错误!

2. 上界通配符(Upper Bounded Wildcard):? extends T

  • 语法:List<? extends Number>
  • 含义:表示T或T的子类的泛型
  • 适用场景:读取数据(生产者场景)
// 计算Number及其子类List的总和
public static double sum(List<? extends Number> list) {
   
    double sum = 0.0;
    for (Number num : list) {
   
        sum += num.doubleValue();
    }
    return sum;
}

// 可以传入Number及其子类的List
sum(new ArrayList<Integer>());
sum(new ArrayList<Double>());
sum(new ArrayList<Long>());

特性

  • 可以安全地读取T类型的数据
  • 不能添加任何元素(除了null),因为编译器无法确定具体的子类类型
List<? extends Number> list = new ArrayList<Integer>();
Number num = list.get(0); // 允许
list.add(123); // 编译错误!

3. 下界通配符(Lower Bounded Wildcard):? super T

  • 语法:List<? super Integer>
  • 含义:表示T或T的父类的泛型
  • 适用场景:写入数据(消费者场景)
// 向List中添加Integer及其子类
public static void addIntegers(List<? super Integer> list) {
   
    list.add(123);
    list.add(456);
}

// 可以传入Integer及其父类的List
addIntegers(new ArrayList<Integer>());
addIntegers(new ArrayList<Number>());
addIntegers(new ArrayList<Object>());

特性

  • 可以安全地添加T及其子类类型的数据
  • 读取数据时只能得到Object类型,因为编译器无法确定具体的父类类型
List<? super Integer> list = new ArrayList<Number>();
list.add(123); // 允许
Object obj = list.get(0); // 只能得到Object
Integer num = list.get(0); // 编译错误!

四、上下界限定的深入理解

4.1 PECS原则(最重要的设计原则)

PECS = Producer Extends, Consumer Super

  • 生产者(Producer):提供数据的对象,使用? extends T(上界通配符)
  • 消费者(Consumer):消费数据的对象,使用? super T(下界通配符)

记忆口诀:"读用extends,写用super,读写都不用通配符"

// 生产者:从src中读取数据,src是生产者
public static <T> void copy(List<? extends T> src, List<? super T> dest) {
   
    for (T t : src) {
   
        dest.add(t);
    }
}

// 使用示例
List<Integer> src = Arrays.asList(1, 2, 3);
List<Number> dest = new ArrayList<>();
copy(src, dest); // 正确

4.2 上下界限定的使用场景对比

通配符类型 语法 允许的类型 读取操作 写入操作 适用场景
无界通配符 ? 任意类型 只能读Object 只能写null 不依赖具体类型的操作
上界通配符 ? extends T T及其子类 可读T 只能写null 生产者(读取数据)
下界通配符 ? super T T及其父类 只能读Object 可写T及其子类 消费者(写入数据)
无通配符 T 具体类型T 可读T 可写T 同时读写数据

4.3 上下界限定的注意事项

  1. 通配符不能用于定义泛型类/接口/方法

    // 编译错误!通配符不能作为类型参数
    class Box<?> {
          ... }
    
    // 正确!使用类型参数T
    class Box<T> {
          ... }
    
  2. 不能同时使用上下界

    // 编译错误!不能同时使用extends和super
    List<? extends Number super Integer> list;
    
  3. 上界通配符的多边界

    // 正确!上界可以有多个,用&分隔
    List<? extends Number & Comparable<?>> list;
    
  4. 下界通配符不能有多个边界

    // 编译错误!下界不能有多个边界
    List<? super Integer & Number> list;
    
  5. 通配符与泛型方法的选择

    • 如果方法的参数之间有依赖关系(如返回值类型依赖于参数类型),使用泛型方法
    • 如果方法的参数之间没有依赖关系,使用通配符更简洁
// 泛型方法:参数和返回值有依赖关系
public static <T> T getFirst(List<T> list) {
   
    return list.get(0);
}

// 通配符:参数之间无依赖关系
public static void printFirst(List<?> list) {
   
    System.out.println(list.get(0));
}

五、常见面试考点与易错点

5.1 核心面试考点

  1. 什么是泛型擦除?为什么Java要使用泛型擦除?
  2. 泛型擦除带来了哪些问题?如何解决?
  3. 什么是桥接方法?为什么需要桥接方法?
  4. 解释PECS原则,并举例说明。
  5. List<Object>List<?>List<? extends Object>有什么区别?
  6. 为什么不能创建泛型数组?
  7. 泛型方法与通配符的区别与联系?
  8. 泛型在集合框架中的应用(如List、Map)?

5.2 常见易错点

  1. 混淆泛型的协变与数组的协变

    // 数组是协变的,允许但不安全
    Object[] arr = new String[10];
    arr[0] = 123; // 运行时ArrayStoreException
    
    // 泛型不协变,编译期就报错
    List<Object> list = new ArrayList<String>(); // 编译错误
    
  2. 错误地使用通配符进行读写操作

    // 错误:上界通配符不能写入
    List<? extends Number> list = new ArrayList<Integer>();
    list.add(123); // 编译错误
    
    // 错误:下界通配符不能安全读取
    List<? super Integer> list = new ArrayList<Number>();
    Integer num = list.get(0); // 编译错误
    
  3. 在泛型类中使用静态类型参数

    class Box<T> {
         
        // 错误:静态变量不能使用类的类型参数
        private static T value;
    }
    
  4. 使用instanceof判断泛型类型

    // 错误:泛型类型被擦除
    if (obj instanceof List<String>) {
          ... }
    
    // 正确:只能判断原始类型
    if (obj instanceof List) {
          ... }
    
  5. 创建泛型数组

    // 错误:不能创建泛型数组
    List<String>[] lists = new List<String>[10];
    
    // 正确:使用List的List
    List<List<String>> lists = new ArrayList<>();
    

六、实战应用示例

6.1 实现一个通用的工具类

public class CollectionUtils {
   
    // 生产者:获取集合中的最大值
    public static <T extends Comparable<? super T>> T max(List<? extends T> list) {
   
        if (list == null || list.isEmpty()) {
   
            throw new IllegalArgumentException("List is null or empty");
        }

        T max = list.get(0);
        for (T t : list) {
   
            if (t.compareTo(max) > 0) {
   
                max = t;
            }
        }
        return max;
    }

    // 消费者:将数组元素添加到集合中
    public static <T> void addAll(List<? super T> list, T[] array) {
   
        for (T t : array) {
   
            list.add(t);
        }
    }

    // 无界通配符:判断集合是否包含指定元素
    public static boolean contains(List<?> list, Object obj) {
   
        return list.contains(obj);
    }
}

6.2 使用示例

public class Main {
   
    public static void main(String[] args) {
   
        List<Integer> intList = Arrays.asList(1, 3, 5, 2, 4);
        System.out.println("Max: " + CollectionUtils.max(intList)); // 5

        List<Number> numList = new ArrayList<>();
        Integer[] intArray = {
   1, 2, 3};
        CollectionUtils.addAll(numList, intArray);
        System.out.println("numList: " + numList); // [1, 2, 3]

        List<String> strList = Arrays.asList("a", "b", "c");
        System.out.println("Contains 'b': " + CollectionUtils.contains(strList, "b")); // true
    }
}

七、知识体系总结

Java泛型的核心是编译期类型检查运行期擦除的结合。泛型擦除保证了向后兼容性,但也带来了一些限制;通配符和上下界限定则在不破坏类型安全的前提下,极大地提高了泛型的灵活性。

掌握PECS原则是正确使用通配符的关键:生产者使用extends,消费者使用super。在实际开发中,应根据具体场景选择合适的泛型形式,以实现代码的复用性、可读性和类型安全。


Java泛型面试核心考点清单(按面试频率排序)

一、最高频考点(★★★★★)

1. 泛型擦除

  • 核心定义:Java泛型是编译期特性,所有泛型类型参数在编译完成后都会被擦除,替换为它们的限定类型(无界则为Object)。运行时JVM并不知道泛型的存在。
  • 设计目的向后兼容JDK 1.4及之前版本,保证泛型代码能在旧版JVM上运行。
  • 擦除规则
    1. 无界类型参数 → 擦除为Object
    2. 有界类型参数 → 擦除为第一个边界类型
    3. 多边界类型参数 → 擦除为第一个边界类型
    4. 泛型方法 → 遵循上述相同规则
  • 带来的5大核心问题及解决方案
问题 解决方案
无法使用instanceof判断泛型类型 使用Class对象作为类型令牌(Type Token)
无法创建泛型数组 使用ArrayList<List<T>>代替,或反射Array.newInstance()
泛型参数不能是基本数据类型 使用对应的包装类
泛型类静态成员不能使用类型参数 静态方法使用独立的泛型方法类型参数
多态性被破坏 编译器自动生成桥接方法(Bridge Method)
  • 桥接方法:当泛型类实现泛型接口或继承泛型父类时,编译器生成的合成方法,用于保证擦除后多态性依然成立。

2. PECS原则

  • 全称:Producer Extends, Consumer Super
  • 核心含义
    • 生产者(提供数据):使用? extends T(上界通配符),只能读不能写(除null)
    • 消费者(消费数据):使用? super T(下界通配符),只能写不能安全读(只能读Object)
  • 记忆口诀读用extends,写用super,读写都不用通配符
  • 经典示例Collections.copy(List<? super T> dest, List<? extends T> src)

3. 三种通配符的区别与使用场景

通配符类型 语法 允许的类型 读取能力 写入能力 适用场景
无界通配符 ? 任意类型 只能读Object 只能写null 不依赖具体类型的操作(如打印、判断包含)
上界通配符 ? extends T T及其所有子类 可读T类型 只能写null 生产者场景(从集合中读取数据)
下界通配符 ? super T T及其所有父类 只能读Object 可写T及其子类 消费者场景(向集合中写入数据)

二、高频考点(★★★★)

1. 泛型的本质与核心优势

  • 本质参数化类型,将数据类型作为参数传递,实现"类型安全的模板机制"
  • 核心优势
    1. 编译期类型安全:避免运行时ClassCastException
    2. 代码复用:一套逻辑处理多种数据类型
    3. 提高可读性:无需频繁强制类型转换
    4. 解耦:数据类型与业务逻辑分离

2. 泛型的三种使用形式

  • 泛型类class Box<T> { ... }(类上定义类型参数)
  • 泛型接口interface List<E> { ... }(接口上定义类型参数)
  • 泛型方法<T> T getValue(Box<T> box) { ... }(方法上独立定义类型参数,与类的泛型无关)

3. List<Object>List<?>List<? extends Object>的区别

  • List<Object>:只能存储Object类型,不能接收List<String>等子类泛型
  • List<?>:可以接收任意类型的List,但只能读Object,只能写null
  • List<? extends Object>:与List<?>完全等价,写法更明确

4. 数组协变 vs 泛型不变

  • 数组是协变的String[]Object[]的子类,但不安全(运行时ArrayStoreException
  • 泛型是不变的List<String>不是List<Object>的子类,编译期就报错,更安全

三、中频考点(★★★)

1. 泛型方法与通配符的选择

  • 使用泛型方法:当方法参数之间或参数与返回值之间有类型依赖关系
  • 使用通配符:当方法参数之间没有类型依赖关系时,代码更简洁

2. 上下界限定的注意事项

  • 通配符不能用于定义泛型类/接口/方法(只能用T等类型参数)
  • 不能同时使用上下界(? extends T super U 编译错误)
  • 上界通配符支持多边界(? extends T & U),下界通配符不支持
  • 泛型方法的类型参数也可以有上下界(<T extends Number>

3. 为什么不能创建泛型数组?

  • 泛型擦除后,数组的类型信息丢失,无法保证运行时类型安全
  • 数组是协变的,而泛型是不变的,两者结合会导致类型安全漏洞

四、低频考点(★★)

1. 泛型在集合框架中的应用

  • 所有Java集合类都使用了泛型(List<E>Set<E>Map<K,V>等)
  • 集合的工具类Collections大量使用了泛型和PECS原则

2. 类型令牌(Type Token)的使用

  • 用于在运行时获取泛型类型信息
  • 示例:public <T> T getBean(String name, Class<T> clazz) { ... }

3. 泛型的继承关系

  • 泛型类的继承:class Child<T> extends Parent<T> { ... }
  • 泛型接口的实现:class MyList<E> implements List<E> { ... }

五、常见面试陷阱(★★★★★)

  1. ❌ 错误:if (obj instanceof List<String>) { ... }
    ✅ 正确:if (obj instanceof List) { ... }

  2. ❌ 错误:List<String>[] lists = new List<String>[10];
    ✅ 正确:List<List<String>> lists = new ArrayList<>();

  3. ❌ 错误:List<? extends Number> list = new ArrayList<>(); list.add(123);
    ✅ 正确:List<Number> list = new ArrayList<>(); list.add(123);

  4. ❌ 错误:List<? super Integer> list = new ArrayList<>(); Integer num = list.get(0);
    ✅ 正确:Object obj = list.get(0);

  5. ❌ 错误:class Box<T> { private static T value; }
    ✅ 正确:class Box<T> { private T value; }

六、面试答题技巧

  1. 回答泛型擦除问题时,一定要提到"向后兼容",这是设计的根本原因
  2. 回答通配符问题时,必须结合PECS原则,并给出具体的读写示例
  3. 遇到对比题(如数组协变vs泛型不变),先讲定义,再讲区别,最后讲安全性
  4. 遇到代码题,先指出错误,再说明原因,最后给出正确写法

Java泛型高频面试题+标准答案速记版

(按面试出现频率排序,每题答案控制在30秒-1分钟内,适合考前突击背诵)

一、必考题(100%会问)

Q1:什么是泛型擦除?为什么Java要使用泛型擦除?

标准答案
泛型擦除是Java泛型的实现机制:泛型信息仅存在于编译期,编译完成后所有类型参数都会被擦除,替换为它们的限定类型(无界则为Object),运行时JVM并不知道泛型的存在。

设计目的向后兼容JDK 1.4及更早版本,保证泛型代码能在旧版JVM上无缝运行,这是Java泛型最核心的设计取舍。


Q2:解释PECS原则,并举例说明。

标准答案
PECS = Producer Extends, Consumer Super,是通配符使用的黄金法则:

  • 生产者(提供数据):使用? extends T(上界通配符),只能安全读取T类型数据,不能写入(除null)
  • 消费者(消费数据):使用? super T(下界通配符),只能安全写入T及其子类数据,读取只能得到Object

经典示例Collections.copy(dest, src)方法,src是生产者用? extends Tdest是消费者用? super T

记忆口诀:读用extends,写用super,读写都不用通配符。


Q3:List<Object>List<?>List<? extends Object>有什么区别?

标准答案

  1. List<Object>:只能存储Object类型,不支持协变,不能接收List<String>等任何子类泛型
  2. List<?>:无界通配符,可以接收任意类型的List,但只能读Object,只能写null
  3. List<? extends Object>:与List<?>完全等价,只是写法更明确,表达"所有继承自Object的类型"

Q4:为什么不能创建泛型数组?

标准答案
两个核心原因:

  1. 类型安全问题:泛型擦除后数组类型信息丢失,无法保证运行时类型安全
  2. 协变与不变的冲突:数组是协变的String[]Object[]的子类),而泛型是不变的List<String>不是List<Object>的子类),两者结合会产生严重的类型安全漏洞

正确替代方案:使用ArrayList<List<T>>代替泛型数组。

二、高频题(80%会问)

Q5:泛型的本质是什么?有什么核心优势?

标准答案
本质参数化类型,将数据类型作为参数传递,实现"类型安全的模板机制"。

核心优势

  1. 编译期类型安全:提前发现类型错误,避免运行时ClassCastException
  2. 代码复用:一套逻辑可处理多种数据类型
  3. 提高可读性:无需频繁强制类型转换
  4. 解耦:数据类型与业务逻辑分离

Q6:什么是桥接方法?为什么需要桥接方法?

标准答案
桥接方法是编译器在泛型类实现泛型接口或继承泛型父类时,自动生成的合成方法

作用:解决泛型擦除后多态性被破坏的问题。例如:

// 接口擦除后
interface Comparable {
    int compareTo(Object o); }

// 实现类
class MyInteger implements Comparable<MyInteger> {
   
    public int compareTo(MyInteger o) {
    ... }
    // 编译器自动生成的桥接方法
    public int compareTo(Object o) {
    return compareTo((MyInteger)o); }
}

Q7:数组协变和泛型不变有什么区别?哪个更安全?

标准答案

  • 数组协变String[]Object[]的子类,允许赋值,但不安全,会在运行时抛出ArrayStoreException
  • 泛型不变List<String>不是List<Object>的子类,编译期就报错,更安全

结论:泛型不变比数组协变更安全,这也是Java泛型设计为不变的重要原因。


Q8:泛型方法和通配符有什么区别?什么时候用哪个?

标准答案

  • 泛型方法:使用<T>定义独立的类型参数,适用于方法参数之间或参数与返回值之间有类型依赖关系的场景
  • 通配符:使用?表示未知类型,适用于方法参数之间没有类型依赖关系的场景,代码更简洁

示例

  • 泛型方法:public <T> T getFirst(List<T> list)(返回值依赖参数类型)
  • 通配符:public void printFirst(List<?> list)(无类型依赖)

三、中频题(50%会问)

Q9:泛型擦除带来了哪些问题?如何解决?

标准答案

问题 解决方案
无法使用instanceof判断泛型类型 使用Class对象作为类型令牌
无法创建泛型数组 使用ArrayList<List<T>>代替
泛型参数不能是基本数据类型 使用对应的包装类
泛型类静态成员不能使用类型参数 静态方法使用独立的泛型方法类型参数
多态性被破坏 编译器自动生成桥接方法

Q10:为什么不能用instanceof判断泛型类型?

标准答案
因为泛型擦除后,运行时JVM只知道原始类型,不知道具体的泛型参数类型。例如List<String>List<Integer>在运行时都是List类型,无法区分。

正确写法:只能判断原始类型if (obj instanceof List)


Q11:泛型有哪三种使用形式?各举一个例子。

标准答案

  1. 泛型类class Box<T> { private T value; }
  2. 泛型接口interface List<E> { boolean add(E e); }
  3. 泛型方法public static <T> T getValue(Box<T> box) { return box.value; }

四、陷阱题(容易答错)

Q12:List<? extends Number>可以添加Integer吗?为什么?

标准答案
不可以。因为List<? extends Number>可以指向List<Integer>List<Double>等任意Number子类的List。如果允许添加Integer,那么当它指向List<Double>时就会出现类型错误。

唯一可以添加的元素是null,因为null是所有引用类型的实例。


Q13:List<? super Integer>可以读取Integer吗?为什么?

标准答案
不可以安全读取。因为List<? super Integer>可以指向List<Integer>List<Number>List<Object>等任意Integer父类的List。读取时编译器无法确定具体类型,只能返回Object。

正确写法Object obj = list.get(0);


Q14:泛型类的静态方法可以使用类的类型参数吗?为什么?

标准答案
不可以。因为静态方法属于类本身,而泛型类的类型参数是在创建实例时才确定的。静态方法在实例创建前就可以调用,此时类型参数还没有被指定。

正确写法:静态方法可以定义自己的泛型类型参数:public static <T> T getElement(T element)

五、面试答题加分技巧

  1. 回答任何泛型问题时,只要涉及擦除,一定要提到"向后兼容",这是Java泛型所有设计取舍的根源
  2. 回答通配符问题时,必须结合PECS原则,并给出具体的读写示例
  3. 遇到代码题,先指出错误,再说明原因,最后给出正确写法
  4. 回答对比题时,先讲定义,再讲区别,最后讲安全性
相关文章
|
1天前
|
SQL 存储 缓存
【Java基础】核心关键字:final、static、volatile、synchronized、transient(附《思维导图》+《面试高频考点清单》)
本文系统解析Java五大核心关键字:final(不可变性)、static(类级共享)、volatile(轻量级可见性)、synchronized(重量级同步)与transient(序列化控制)。涵盖语义、使用场景、底层原理(内存屏障、Monitor、类加载机制等)、常见误区及高频面试要点。
【Java基础】核心关键字:final、static、volatile、synchronized、transient(附《思维导图》+《面试高频考点清单》)
|
1天前
|
存储 安全 Java
【Java基础】String不可变性、String vs StringBuffer vs StringBuilder、常量池、intern()方法(附《思维导图》+《面试考点背诵版》)
本文系统解析Java字符串核心机制:String不可变性(`final char[]`实现)、String/StringBuffer/StringBuilder三者对比(可变性/线程安全/性能)、字符串常量池(JDK7+移至堆)及`intern()`方法(JDK7+直接存堆引用)。
|
1天前
|
Java 编译器 程序员
【Java基础】异常体系:Error vs Exception、受检/非受检异常、try-catch-finally、try-with-resources(附《思维导图》+《面试高频考点清单》)
本文系统梳理Java异常体系:以`Throwable`为根,分`Error`(JVM级不可恢复错误)与`Exception`(可处理异常);后者再分为编译期强制处理的**受检异常**(如`IOException`)和运行时抛出的**非受检异常**(如`NullPointerException`),并详解`try-catch-finally`、`try-with-resources`、异常链及最佳实践。
|
1天前
|
缓存 算法 搜索推荐
程序员必备的十大技能(进阶版)之高阶数据结构与算法(四)
教程来源 http://qcycj.cn/ 本节介绍海量数据与字符串匹配核心算法:布隆过滤器(高效判存、允许误报)、倒排索引(支撑搜索引擎的词→文档映射)、KMP(线性单模匹配)及AC自动机(O(n)多模匹配)。兼顾原理、代码实现与典型场景,适用于缓存穿透防护、URL去重、全文检索与敏感词识别等工业级应用。
|
1天前
|
人工智能 开发工具 开发者
终端里跑 3D 老鼠,桌面窗口成摆锤;AI 大佬新公司估值百亿起
上周技术圈的信息挺杂,但有几条线索值得放在一起看。 一边,AI 产品继续往具体工作流里走:Claude Code 开始支持 Agent View,OpenAI 把 Codex 带到移动端;另一边,开发者社区继续整活:有人给 Claude Code 做实体旋钮,有人做 Claude 用量桌面仪表盘,还有人把终端做成能显示 3D 老鼠的玩具。
终端里跑 3D 老鼠,桌面窗口成摆锤;AI 大佬新公司估值百亿起
|
1天前
|
SQL Java 数据库
[019][数据模块]MyBatis-Plus 拦截器扩展设计:基于函数式接口与 Spring 自动装配
本文介绍基于函数式接口`InnerInterceptorSupplier`与Spring `ObjectProvider`的MyBatis-Plus拦截器自动装配方案,支持按`@Order`声明式排序、延迟创建及模块化扩展,提升分页、乐观锁等能力的可插拔性与框架集成友好度。(239字)
31 0
|
1天前
|
人工智能 开发框架 自然语言处理
企业级Java AI开发框架核心模块:从资源整合到场景落地
JBoltAI是企业级Java AI应用开发框架,聚焦业务闭环落地,非工具堆砌。含五大模块:AI资源网关(统管20+模型)、智能数据治理(盘活私有数据)、企业本体语义模型(构建业务知识孪生)、零低代码应用开发、Agent数字员工平台,助力企业AI重塑与原生开发。(239字)
|
1天前
|
存储 弹性计算 安全
阿里云99元和199元云服务器怎么买更划算?最新专属组合套餐配置与价格参考
阿里云99元(e实例,2核2G/3M/40G)和199元(u1实例,2核4G/5M/80G)云服务器是个人开发者与中小企业的高性价比之选,活动截止2027年3月31日,新购续费同价。最优选购策略是搭配组合套餐:加99元可解锁弹性数据库(ECS+RDS 198元起)或高效存储(ECS+OSS 217.99元起);建站用户可选域名+ECS+建站礼包(460元起)或域名+ECS+SSL证书(525元起),建议通过选购组合套餐形式,一站式采购,实现省心降本。
|
1天前
|
SQL 关系型数据库 MySQL
批量操作进阶:百万行级数据导入的性能极限
本文分享百万行数据导入四大进阶技巧:分区表减少锁竞争、禁用索引加速写入、并行LOAD DATA榨干多核性能、金仓kdb_load专用工具再提速。实测100万行最快&lt;1秒,助你从分钟级跃升秒级!
|
1天前
|
算法 程序员 调度
程序员必备的十大技能(进阶版)之高阶数据结构与算法(五)
教程来源 http://oplhc.cn/ 本节系统讲解动态规划与贪心策略:DP以状态定义、转移方程、初始条件和遍历顺序为核心,涵盖LCS、0-1/完全/多重背包;贪心需严格证明(如交换论证),典型应用包括区间调度、哈夫曼编码。同时对比分治、回溯、分支限界与随机化算法,构建完整算法设计认知体系。

热门文章

最新文章