Java基础篇——第二部

28 阅读51分钟

第四章:泛型

4.1 什么是泛型?为什么需要泛型?

4.1.1 泛型的基本概念和作用

定义:泛型是Java 5引入的一种参数化类型机制,允许在定义类、接口、方法时使用类型参数。

核心概念

  • 类型参数:用尖括号<>表示,如<T>
  • 类型实参:使用时传入的具体类型,如String
  • 泛型类型:带有类型参数的类或接口

基本示例

java

// 不使用泛型
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);  // 需要强制类型转换

// 使用泛型
List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0);  // 自动类型推断,无需强制转换

4.1.2 泛型带来的好处

类型安全

  • 编译时类型检查,避免运行时ClassCastException
  • 错误在编译期就能发现,而不是运行时

代码复用

  • 编写一次,多种类型使用
  • 减少代码重复,提高可维护性

代码简洁

  • 消除强制类型转换
  • 代码更易读、更清晰

对比示例

方面无泛型有泛型
类型安全运行时可能抛出ClassCastException编译时检查,类型安全
类型转换需要显式强制类型转换自动类型推断,无需转换
代码简洁性代码冗长,可读性差代码简洁,意图明确
错误检测运行时才能发现类型错误编译时就能发现错误

java

// 不使用泛型 - 可能出错
List list = new ArrayList();
list.add("hello");
list.add(123);  // 可以添加不同类型
String s = (String) list.get(1);  // 运行时抛出ClassCastException

// 使用泛型 - 类型安全
List<String> list = new ArrayList<>();
list.add("hello");
// list.add(123);  // 编译错误:无法添加Integer
String s = list.get(0);  // 安全,自动类型推断

4.1.3 泛型的历史背景

发展历程

时间版本特性问题
Java 1.0-1.4无泛型使用Object和强制类型转换类型不安全,代码冗长
2004年Java 5引入泛型编译时类型检查
设计目标-向后兼容,类型安全引入类型擦除实现兼容

设计目标

  1. 向后兼容:老代码无需修改就能运行
  2. 类型安全:编译时检查类型错误
  3. 代码复用:一套代码多种类型使用

4.1.4 泛型在集合框架中的应用

集合框架的泛型化

集合类无泛型版本有泛型版本主要改进
ListListList<E>元素类型安全
SetSetSet<E>元素类型安全
MapMapMap<K,V>键值类型安全
IteratorIteratorIterator<E>迭代类型安全

集合框架示例对比

java

// Java 5之前:不使用泛型
Map map = new HashMap();
map.put("key", "value");
map.put(123, "number");  // 可以混合类型
String value = (String) map.get("key");  // 需要强制转换
// String wrong = (String) map.get(123);  // 运行时错误

// Java 5之后:使用泛型
Map<String, String> map = new HashMap<>();
map.put("key", "value");
// map.put(123, "number");  // 编译错误:类型不匹配
String value = map.get("key");  // 自动类型推断,无需转换

总结

  • 泛型是Java的类型参数化机制
  • 主要优点:类型安全、代码复用、消除强制转换
  • 集合框架是泛型最主要的应用场景
  • 泛型让代码更安全、更简洁、更易维护

4.2 泛型擦除是什么?为什么要擦除?

4.2.1 泛型擦除的概念和表现

定义:泛型擦除是Java泛型的实现机制,编译器在编译期间移除所有泛型类型信息,生成的字节码中不包含泛型。

表现

  1. 运行时无法获取泛型的具体类型
  2. 泛型类型参数被替换为原始类型(Object或边界类型)
  3. 编译时检查,运行时擦除

示例

java

// 源代码
List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0);

// 编译后(概念上的等效代码)
List list = new ArrayList();  // 泛型被擦除
list.add("hello");
String s = (String) list.get(0);  // 插入强制类型转换

擦除验证

java

// 验证泛型擦除
List<String> stringList = new ArrayList<>();
List<Integer> integerList = new ArrayList<>();

// 检查运行时类型
System.out.println(stringList.getClass());  // class java.util.ArrayList
System.out.println(integerList.getClass()); // class java.util.ArrayList
// 两者类型相同,说明泛型信息被擦除

// 以下代码在运行时是合法的(通过反射绕过编译检查)
List list = stringList;
list.add(123);  // 编译时会有警告,但运行时能添加Integer
System.out.println(stringList.get(1));  // 运行时抛出ClassCastException

4.2.2 泛型擦除的底层实现原理

擦除过程

泛型类型擦除后类型说明
List<T>List无界类型参数擦除为Object
List<? extends Number>List上界通配符擦除为上界类型
List<? super Integer>List下界通配符擦除为Object
List<String>List具体类型擦除为原始类型

擦除示例

java

// 泛型类定义
public class Box<T> {
    private T value;
    
    public void set(T value) {
        this.value = value;
    }
    
    public T get() {
        return value;
    }
}

// 擦除后的等效代码(概念上)
public class Box {
    private Object value;  // T被擦除为Object
    
    public void set(Object value) {
        this.value = value;
    }
    
    public Object get() {
        return value;
    }
}

4.2.3 为什么需要泛型擦除(向后兼容)

核心原因:保持向后兼容性

Java版本兼容性对比

版本特性兼容性问题
Java 1.0-1.4无泛型-
Java 5引入泛型需要兼容老版本代码

兼容性解决方案

  1. 源代码兼容:老代码无需修改能编译
  2. 二进制兼容:老class文件能在新JVM运行
  3. 迁移兼容:混合使用泛型和非泛型代码

示例

java

// Java 1.4代码(无泛型)
List list = new ArrayList();
list.add("old code");

// Java 5+代码(有泛型)
List<String> newList = new ArrayList<>();

// 两者可以混合使用(为了兼容)
list = newList;  // 允许,但有警告
newList = list;  // 允许,但有警告

4.2.4 擦除带来的限制

主要限制

限制描述示例
无法实例化不能new T()T obj = new T(); ❌
无法创建数组不能new T[]T[] array = new T[10]; ❌
无法使用instanceof不能检查泛型类型obj instanceof List<String> ❌
静态成员共享静态变量被所有实例共享static T value; ❌
无法重载擦除后方法签名相同void method(List<String>)void method(List<Integer>) ❌

限制示例

java

public class Limitations<T> {
    // 1. 不能实例化类型参数
    // T obj = new T();  // 编译错误
    
    // 2. 不能创建泛型数组
    // T[] array = new T[10];  // 编译错误
    
    // 3. 静态上下文中不能使用类型参数
    // static T value;  // 编译错误
    
    // 4. instanceof不能用于泛型类型
    public boolean isStringList(Object obj) {
        // return obj instanceof List<String>;  // 编译错误
        return obj instanceof List;  // 只能这样
    }
    
    // 5. 类型参数不能用于异常
    // class MyException<T> extends Exception {}  // 编译错误
}

4.2.5 桥方法(Bridge Method)机制

概念:编译器生成的方法,用于在泛型擦除后保持多态性。

产生场景:泛型类或接口被继承/实现时

示例

java

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

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

// 编译后生成的桥方法
class StringComparable implements Comparable<String> {
    // 实际的方法
    public int compareTo(String other) {
        return 0;
    }
    
    // 编译器生成的桥方法(为了兼容擦除)
    public int compareTo(Object other) {
        return compareTo((String) other);  // 调用实际的方法
    }
}

桥方法的作用

  1. 保持Java泛型的多态性
  2. 确保子类正确重写父类方法
  3. 处理类型擦除带来的方法签名变化

查看桥方法

java

Method[] methods = StringComparable.class.getDeclaredMethods();
for (Method method : methods) {
    System.out.println(method.getName() + ": " + method.isBridge());
}
// 输出包含桥方法的信息

总结

  • 泛型擦除是Java泛型的实现机制,为了向后兼容
  • 优点:兼容老代码,运行时性能几乎无影响
  • 缺点:带来诸多限制,运行时无法获取类型信息
  • 桥方法:编译器自动生成,保证多态性
  • 设计权衡:在类型安全和兼容性之间取得平衡

4.3 Java泛型中类型擦除的理解及局限

4.3.1 泛型的8个使用限制详解

完整限制列表

序号限制描述代码示例
1不能实例化类型参数无法new T()T obj = new T(); ❌
2不能创建泛型数组无法new T[]T[] arr = new T[10]; ❌
3静态成员不能使用类型参数静态域或方法中不能用Tstatic T value; ❌
4instanceof不能用于泛型类型无法检查List<String>obj instanceof List<String> ❌
5基本类型不能作为类型参数必须使用包装类List<int> ❌,List<Integer> ✅
6不能创建异常泛型类异常类不能有类型参数class MyEx<T> extends Exception ❌
7不能重载擦除后相同的方法方法签名擦除后相同void m(List<String>)void m(List<Integer>) ❌
8不能捕获泛型异常catch不能捕获类型参数catch (T e) ❌

4.3.2 不能使用基本类型的原因

原因分析

  1. 泛型擦除机制:类型参数擦除为Object或其边界类型
  2. Object不能存储基本类型:需要自动装箱/拆箱
  3. 性能考虑:避免自动装箱带来的性能开销

对比示例

java

// 错误:不能使用基本类型
// List<int> intList = new ArrayList<>();  // 编译错误

// 正确:使用包装类
List<Integer> integerList = new ArrayList<>();

// 自动装箱和拆箱
integerList.add(123);  // 自动装箱 int → Integer
int value = integerList.get(0);  // 自动拆箱 Integer → int

// 性能对比
long start = System.nanoTime();
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
    list.add(i);  // 自动装箱,有性能开销
}
long end = System.nanoTime();
System.out.println("泛型列表耗时: " + (end - start) + " ns");

// 使用基本类型数组性能更好
start = System.nanoTime();
int[] array = new int[1000000];
for (int i = 0; i < 1000000; i++) {
    array[i] = i;  // 直接赋值,无装箱开销
}
end = System.nanoTime();
System.out.println("基本数组耗时: " + (end - start) + " ns");

4.3.3 泛型数组的限制

限制原因

  1. 类型安全:数组是协变的,运行时检查元素类型
  2. 擦除冲突:泛型擦除后类型信息丢失,无法进行数组类型检查

错误示例

java

// 1. 不能直接创建泛型数组
// List<String>[] array = new List<String>[10];  // 编译错误

// 2. 这样也不可以
// T[] array = new T[10];  // 编译错误

// 3. 为什么不行?类型安全问题
// 假设允许:
// List<String>[] stringLists = new List<String>[1];  // 假设允许
// List<Integer> intList = Arrays.asList(42);
// Object[] objects = stringLists;  // 数组是协变的,允许向上转型
// objects[0] = intList;  // 可以赋值,因为Object[]可以存储任何Object
// String s = stringLists[0].get(0);  // 运行时错误:ClassCastException

解决方案

java

// 方案1:使用Object[]然后强制转换(不安全)
List<String>[] array = (List<String>[]) new List[10];  // 警告,但不报错
array[0] = new ArrayList<String>();

// 方案2:使用ArrayList替代数组(推荐)
List<List<String>> listOfLists = new ArrayList<>();
listOfLists.add(new ArrayList<>());

// 方案3:使用反射创建数组(复杂,类型不安全)
public static <T> T[] createArray(Class<T> type, int size) {
    return (T[]) Array.newInstance(type, size);
}

String[] stringArray = createArray(String.class, 10);

4.3.4 instanceof不能用于泛型类型

限制原因

  1. 类型擦除:运行时无法获取泛型类型信息
  2. instanceof需要运行时类型信息

错误示例

java

List<String> list = new ArrayList<>();

// 错误:不能检查具体泛型类型
// if (list instanceof List<String>) {  // 编译错误
//     System.out.println("是字符串列表");
// }

// 只能检查原始类型
if (list instanceof List) {  // 正确,但不够精确
    System.out.println("是List,但不知道元素类型");
}

// 解决方案:检查元素类型
if (!list.isEmpty() && list.get(0) instanceof String) {
    System.out.println("列表元素是String类型");
}

4.3.5 不能创建泛型实例

限制原因:类型擦除导致运行时不知道T的具体类型

错误示例

java

class Container<T> {
    // 错误:不能直接实例化类型参数
    // public T createInstance() {
    //     return new T();  // 编译错误
    // }
    
    // 错误:也不能创建数组
    // public T[] createArray() {
    //     return new T[10];  // 编译错误
    // }
}

解决方案

java

// 方案1:传入Class对象
class Container<T> {
    private Class<T> type;
    
    public Container(Class<T> type) {
        this.type = type;
    }
    
    public T createInstance() throws Exception {
        return type.newInstance();  // 使用反射创建实例
    }
    
    public T[] createArray(int size) {
        return (T[]) Array.newInstance(type, size);
    }
}

Container<String> container = new Container<>(String.class);
String str = container.createInstance();  // 创建String实例
String[] array = container.createArray(10);  // 创建String数组

4.3.6 静态上下文中不能使用类型参数

限制原因

  1. 静态成员属于类,而不是实例
  2. 类型参数属于实例
  3. 不同实例可能有不同的类型实参

错误示例

java

class Box<T> {
    // 错误:静态变量不能使用类型参数
    // private static T value;  // 编译错误
    
    // 错误:静态方法不能使用类的类型参数
    // public static T getValue() {  // 编译错误
    //     return value;
    // }
    
    // 正确:静态方法可以有自己独立的类型参数
    public static <U> U staticMethod(U param) {
        return param;
    }
}

正确用法

java

class Utils {
    // 泛型静态方法
    public static <T> T getFirst(List<T> list) {
        return list.isEmpty() ? null : list.get(0);
    }
    
    // 多个类型参数的静态方法
    public static <K, V> Map<K, V> createMap(K key, V value) {
        Map<K, V> map = new HashMap<>();
        map.put(key, value);
        return map;
    }
}

// 使用
String first = Utils.getFirst(Arrays.asList("a", "b", "c"));
Map<Integer, String> map = Utils.createMap(1, "one");

总结表格

限制原因解决方案
不能实例化T运行时类型信息擦除传入Class对象,使用反射
不能创建泛型数组数组协变和类型安全使用集合或反射创建数组
instanceof限制运行时无泛型信息检查元素类型而非容器类型
不能使用基本类型Object不能存储基本类型使用包装类,注意性能
静态成员限制类型参数属于实例静态方法用独立类型参数

核心理解

  • 泛型是编译时概念,运行时信息被擦除
  • 限制源于类型擦除和Java的类型系统设计
  • 理解限制有助于避免常见错误,写出正确的泛型代码

4.4 什么是通配符?上界通配符和下界通配符的区别

4.4.1 无界通配符(?)

定义?表示未知类型,可以匹配任何类型。

使用场景

  1. 只关心容器操作,不关心元素类型
  2. 使用Object类中的方法操作元素
  3. 读取时返回Object,写入时受限

示例

java

// 无界通配符参数
public void processList(List<?> list) {
    // 可以读取,返回Object
    Object obj = list.get(0);
    
    // 不能添加元素(除了null)
    // list.add("string");  // 编译错误,类型未知
    list.add(null);  // 允许,null可以赋值给任何类型
    
    // 可以调用与类型无关的方法
    System.out.println("Size: " + list.size());
    list.clear();
}

// 使用
List<String> stringList = Arrays.asList("a", "b");
List<Integer> intList = Arrays.asList(1, 2, 3);

processList(stringList);  // 可以处理任何类型的List
processList(intList);

4.4.2 上界通配符(? extends T)

定义? extends T表示T或T的子类。

特点

  • 安全读取:可以安全地读取为T类型
  • 限制写入:不能添加任何元素(除了null)

示例

java

// 上界通配符
public double sum(List<? extends Number> numbers) {
    double total = 0;
    for (Number n : numbers) {  // 安全读取为Number
        total += n.doubleValue();
    }
    
    // 不能添加元素
    // numbers.add(new Integer(10));  // 编译错误
    // numbers.add(new Double(3.14));  // 编译错误
    numbers.add(null);  // 允许
    
    return total;
}

// 使用
List<Integer> integers = Arrays.asList(1, 2, 3);
List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3);

System.out.println(sum(integers));  // 6.0
System.out.println(sum(doubles));   // 6.6

// 也可以传入Number列表
List<Number> numbers = Arrays.asList(1, 2.5, 3);
System.out.println(sum(numbers));   // 6.5

4.4.3 下界通配符(? super T)

定义? super T表示T或T的父类。

特点

  • 安全写入:可以添加T类型及其子类的元素
  • 限制读取:读取时只能得到Object类型

示例

java

// 下界通配符
public void addNumbers(List<? super Integer> list) {
    // 可以安全地添加Integer及其子类
    list.add(10);
    list.add(20);
    
    // 也可以添加Integer的子类(如果有)
    // list.add(30);  // 自动装箱为Integer
    
    // 读取时只能得到Object
    Object obj = list.get(0);
    
    // 需要强制类型转换才能得到具体类型
    if (obj instanceof Integer) {
        Integer num = (Integer) obj;
        System.out.println("Got integer: " + num);
    }
}

// 使用
List<Number> numbers = new ArrayList<>();
addNumbers(numbers);  // 可以添加Integer到Number列表
System.out.println(numbers);  // [10, 20]

List<Object> objects = new ArrayList<>();
addNumbers(objects);  // 可以添加Integer到Object列表
System.out.println(objects);  // [10, 20]

// List<String> strings = new ArrayList<>();
// addNumbers(strings);  // 编译错误:String不是Integer的父类

4.4.4 PECS原则(Producer Extends, Consumer Super)

PECS原则

  • Producer Extends:生产者(提供数据)使用extends
  • Consumer Super:消费者(接收数据)使用super

记忆口诀

  • 需要从集合数据(生产数据):用extends
  • 需要向集合数据(消费数据):用super
  • 既取又存:不要用通配符,用具体类型

PECS应用示例

java

// 1. 生产者 - 只读
public static double sumOfList(List<? extends Number> list) {
    double s = 0.0;
    for (Number n : list) {  // 生产Number对象
        s += n.doubleValue();
    }
    return s;
}

// 2. 消费者 - 只写
public static void addIntegers(List<? super Integer> list) {
    for (int i = 1; i <= 5; i++) {
        list.add(i);  // 消费Integer对象
    }
}

// 3. 既生产又消费 - 不用通配符
public static <T> void copy(List<T> dest, List<T> src) {
    for (T element : src) {  // 生产T
        dest.add(element);   // 消费T
    }
}

// 4. 更灵活的拷贝(PECS版)
public static <T> void copyPECS(List<? super T> dest, List<? extends T> src) {
    for (T element : src) {  // src生产T
        dest.add(element);   // dest消费T
    }
}

// 使用PECS拷贝
List<Number> numbers = new ArrayList<>();
List<Integer> integers = Arrays.asList(1, 2, 3);
copyPECS(numbers, integers);  // 可以复制Integer到Number列表
System.out.println(numbers);  // [1, 2, 3]

4.4.5 通配符捕获

概念:通配符?表示未知类型,但有时需要引用这个未知类型。

问题:不能直接使用?作为类型变量

错误示例

java

// 错误:不能直接使用?
public static void swap(List<?> list, int i, int j) {
    // 不能直接声明?类型的变量
    // ? temp = list.get(i);  // 编译错误
    
    // 需要通配符捕获
    swapHelper(list, i, j);
}

解决方案:通配符捕获辅助方法

java

// 辅助方法捕获通配符
private static <E> void swapHelper(List<E> list, int i, int j) {
    E temp = list.get(i);  // 这里可以声明E类型的变量
    list.set(i, list.get(j));
    list.set(j, temp);
}

// 使用
List<String> list = Arrays.asList("a", "b", "c");
swapHelper(list, 0, 2);  // 交换位置0和2
System.out.println(list);  // [c, b, a]

4.4.6 通配符的使用场景

场景对比总结

场景推荐通配符示例说明
只读不写? extends Tvoid print(List<? extends Number>)安全读取,限制写入
只写不读? super Tvoid fill(List<? super Integer>)安全写入,限制读取
读写都需要不用通配符<T> void copy(List<T> src, List<T> dest)类型参数提供灵活性
完全类型无关?int size(Collection<?> coll)只关心集合操作
PECS模式组合使用<T> void copy(List<? super T> dest, List<? extends T> src)生产消费分离

实际应用示例

java

// Java Collections API中的通配符使用

// 1. Collections.copy() - PECS原则
// public static <T> void copy(List<? super T> dest, List<? extends T> src)

// 2. Collections.addAll() - 下界通配符
// public static <T> boolean addAll(Collection<? super T> c, T... elements)

// 3. Collections.max() - 上界通配符
// public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)

// 自定义示例
class DataProcessor {
    // 场景1:处理任何类型的集合
    public static void processAny(Collection<?> collection) {
        System.out.println("Processing " + collection.size() + " elements");
    }
    
    // 场景2:统计数值型集合的总和
    public static double sumCollection(Collection<? extends Number> numbers) {
        return numbers.stream()
                      .mapToDouble(Number::doubleValue)
                      .sum();
    }
    
    // 场景3:向集合添加默认值
    public static <T> void addDefaults(Collection<? super T> collection, T defaultValue, int count) {
        for (int i = 0; i < count; i++) {
            collection.add(defaultValue);
        }
    }
}

总结

  • 无界通配符? :类型完全未知,限制写入
  • 上界通配符? extends T:生产者,只读不写
  • 下界通配符? super T:消费者,只写不读
  • PECS原则:指导何时使用哪种通配符
  • 通配符捕获:通过辅助方法处理未知类型
  • 使用场景:根据读写需求选择合适的通配符

4.5 泛型中K、T、V、E、?等符号的含义

4.5.1 类型参数的命名规范

Java泛型命名约定

符号含义常用场景
TType(类型)通用类型参数
EElement(元素)集合中的元素类型
KKey(键)Map的键类型
VValue(值)Map的值类型
NNumber(数字)数值类型
?通配符未知类型
RReturn(返回值)方法返回值类型
U, S第二、第三类型多个类型参数

命名规则

  1. 单大写字母,简洁明了
  2. 有意义的字母表示用途
  3. 多个类型参数按字母顺序:T, U, V, S等
  4. 通配符?单独使用,表示未知类型

4.5.2 常见泛型符号的含义

T (Type) - 最常用的类型参数

java

// 通用容器类
class Container<T> {
    private T value;
    
    public void set(T value) {
        this.value = value;
    }
    
    public T get() {
        return value;
    }
}

// 使用
Container<String> stringContainer = new Container<>();
stringContainer.set("Hello");
String s = stringContainer.get();

E (Element) - 集合元素类型

java

// 自定义集合接口
interface Stack<E> {
    void push(E element);
    E pop();
    boolean isEmpty();
}

// 实现
class ArrayStack<E> implements Stack<E> {
    private List<E> elements = new ArrayList<>();
    
    @Override
    public void push(E element) {
        elements.add(element);
    }
    
    @Override
    public E pop() {
        return elements.remove(elements.size() - 1);
    }
    
    // 其他方法...
}

K, V (Key, Value) - Map的键值类型

java

// 自定义Map条目
class Entry<K, V> {
    private K key;
    private V value;
    
    public Entry(K key, V value) {
        this.key = key;
        this.value = value;
    }
    
    public K getKey() { return key; }
    public V getValue() { return value; }
}

// 使用
Entry<String, Integer> entry = new Entry<>("age", 25);
String key = entry.getKey();    // String
Integer value = entry.getValue(); // Integer

N (Number) - 数值类型参数

java

// 数值处理器
class NumberProcessor<N extends Number> {
    private N number;
    
    public NumberProcessor(N number) {
        this.number = number;
    }
    
    public double getDoubleValue() {
        return number.doubleValue();
    }
}

// 使用
NumberProcessor<Integer> intProcessor = new NumberProcessor<>(100);
NumberProcessor<Double> doubleProcessor = new NumberProcessor<>(3.14);

R (Return) - 返回值类型

java

// 函数式接口中的返回值类型
@FunctionalInterface
interface Function<T, R> {
    R apply(T input);
}

// 使用
Function<String, Integer> stringToLength = String::length;
int length = stringToLength.apply("hello");  // 5

4.5.3 多个类型参数的使用

多个类型参数示例

java

// 两个类型参数
class Pair<T, U> {
    private T first;
    private U second;
    
    public Pair(T first, U second) {
        this.first = first;
        this.second = second;
    }
    
    public T getFirst() { return first; }
    public U getSecond() { return second; }
}

// 三个类型参数
class Triple<T, U, V> {
    private T first;
    private U second;
    private V third;
    
    // 构造函数、getter、setter...
}

// 使用
Pair<String, Integer> nameAndAge = new Pair<>("Alice", 30);
Triple<String, Integer, Boolean> triple = new Triple<>("Bob", 25, true);

复杂示例:Map模拟

java

class SimpleMap<K, V> {
    private List<Pair<K, V>> entries = new ArrayList<>();
    
    public void put(K key, V value) {
        // 简单实现,不考虑key重复
        entries.add(new Pair<>(key, value));
    }
    
    public V get(K key) {
        for (Pair<K, V> entry : entries) {
            if (entry.getFirst().equals(key)) {
                return entry.getSecond();
            }
        }
        return null;
    }
}

// 使用
SimpleMap<String, Integer> scores = new SimpleMap<>();
scores.put("Alice", 95);
scores.put("Bob", 87);
System.out.println(scores.get("Alice"));  // 95

4.5.4 泛型方法和泛型类

泛型类 vs 泛型方法对比

方面泛型类泛型方法
定义位置类/接口声明处方法声明处
作用范围整个类/接口单个方法
类型参数类级别,实例共享方法级别,独立
静态方法不能使用类的类型参数可以有独立的类型参数
使用场景容器类、数据结构工具方法、算法

泛型类示例

java

// 泛型类
class Box<T> {
    private T content;
    
    public Box(T content) {
        this.content = content;
    }
    
    public T getContent() {
        return content;
    }
    
    // 注意:静态方法不能使用T
    public static <U> Box<U> createBox(U content) {
        return new Box<>(content);
    }
}

泛型方法示例

java

// 工具类中的泛型方法
class ArrayUtils {
    // 泛型方法:交换数组元素
    public static <T> void swap(T[] array, int i, int j) {
        T temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
    
    // 泛型方法:查找最大值
    public static <T extends Comparable<T>> T max(T[] array) {
        if (array == null || array.length == 0) {
            return null;
        }
        T max = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array[i].compareTo(max) > 0) {
                max = array[i];
            }
        }
        return max;
    }
    
    // 多个类型参数的泛型方法
    public static <K, V> String formatPair(K key, V value) {
        return key + " -> " + value;
    }
}

// 使用
String[] names = {"Alice", "Bob", "Charlie"};
ArrayUtils.swap(names, 0, 2);  // 交换第一个和最后一个

Integer[] numbers = {3, 1, 4, 1, 5, 9};
Integer maxNumber = ArrayUtils.max(numbers);  // 9

String formatted = ArrayUtils.formatPair("age", 25);

4.5.5 泛型接口

泛型接口定义

java

// 泛型接口
interface Repository<T> {
    void save(T entity);
    T findById(String id);
    List<T> findAll();
    void delete(T entity);
}

// 实现泛型接口
class UserRepository implements Repository<User> {
    private List<User> users = new ArrayList<>();
    
    @Override
    public void save(User user) {
        users.add(user);
    }
    
    @Override
    public User findById(String id) {
        return users.stream()
                   .filter(user -> user.getId().equals(id))
                   .findFirst()
                   .orElse(null);
    }
    
    @Override
    public List<User> findAll() {
        return new ArrayList<>(users);
    }
    
    @Override
    public void delete(User user) {
        users.remove(user);
    }
}

// 用户类
class User {
    private String id;
    private String name;
    
    // 构造器、getter、setter...
}

泛型接口的多种实现方式

java

// 方式1:实现时指定具体类型
class StringRepository implements Repository<String> {
    // 实现方法...
}

// 方式2:保持泛型,实现类也是泛型类
class GenericRepository<T> implements Repository<T> {
    private List<T> items = new ArrayList<>();
    
    @Override
    public void save(T item) {
        items.add(item);
    }
    
    // 其他方法实现...
}

// 方式3:实现有界泛型接口
interface ComparableRepository<T extends Comparable<T>> extends Repository<T> {
    List<T> findSorted();
}

class ComparableUserRepository implements ComparableRepository<User> {
    // 需要实现Repository的所有方法
    // 以及ComparableRepository的findSorted方法
}

总结表格

符号含义典型用法示例
T通用类型类、方法、接口的类型参数class Box<T>
E元素类型集合框架List<E>Set<E>
K键类型Map的键Map<K, V>
V值类型Map的值Map<K, V>
N数值类型数值处理N extends Number
R返回值函数式接口Function<T, R>
?通配符未知类型List<?>
U, S第二、三类型多个类型参数Pair<T, U>

最佳实践

  1. 遵循命名约定,提高代码可读性
  2. 根据需要选择泛型类或泛型方法
  3. 泛型接口提供灵活的抽象
  4. 合理使用有界类型参数增加类型安全
  5. 泛型方法特别适合工具类实现

4.6 泛型高级特性

4.6.1 泛型与反射

反射获取泛型信息

  • 运行时类型擦除导致无法直接获取泛型类型
  • 通过反射的ParameterizedType可以获取部分泛型信息

获取泛型信息的三种方式

java

import java.lang.reflect.*;
import java.util.*;

// 1. 获取类的泛型信息
class GenericClass<T> {
    private T value;
}

// 2. 获取字段的泛型信息
class TypeExample {
    private List<String> stringList;
    private Map<String, Integer> map;
}

// 3. 获取方法的泛型信息
class MethodExample {
    public List<String> getStringList() {
        return new ArrayList<>();
    }
}

public class GenericReflection {
    public static void main(String[] args) throws Exception {
        // 1. 获取类的泛型参数
        Type type = GenericClass.class.getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) type;
            Type[] actualTypes = pt.getActualTypeArguments();
            System.out.println("类泛型参数: " + Arrays.toString(actualTypes));
        }
        
        // 2. 获取字段的泛型类型
        Field field = TypeExample.class.getDeclaredField("stringList");
        Type fieldType = field.getGenericType();
        if (fieldType instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) fieldType;
            System.out.println("字段类型: " + pt.getRawType());
            System.out.println("泛型参数: " + Arrays.toString(pt.getActualTypeArguments()));
        }
        
        // 3. 获取方法的返回类型
        Method method = MethodExample.class.getMethod("getStringList");
        Type returnType = method.getGenericReturnType();
        if (returnType instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) returnType;
            System.out.println("方法返回类型: " + pt.getRawType());
            System.out.println("泛型参数: " + Arrays.toString(pt.getActualTypeArguments()));
        }
        
        // 4. 获取匿名内部类的泛型信息
        List<String> list = new ArrayList<String>() {};
        Type listType = list.getClass().getGenericSuperclass();
        if (listType instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) listType;
            System.out.println("匿名类泛型: " + Arrays.toString(pt.getActualTypeArguments()));
        }
    }
}

泛型数组的反射创建

java

public class GenericArrayReflection {
    
    // 创建泛型数组的通用方法
    @SuppressWarnings("unchecked")
    public static <T> T[] createGenericArray(Class<T> type, int length) {
        return (T[]) Array.newInstance(type, length);
    }
    
    // 获取数组元素的类型
    public static Class<?> getComponentType(Object array) {
        return array.getClass().getComponentType();
    }
    
    public static void main(String[] args) {
        // 创建泛型数组
        String[] stringArray = createGenericArray(String.class, 10);
        Integer[] intArray = createGenericArray(Integer.class, 5);
        
        // 填充数组
        Arrays.fill(stringArray, "hello");
        for (int i = 0; i < intArray.length; i++) {
            intArray[i] = i * i;
        }
        
        System.out.println("字符串数组: " + Arrays.toString(stringArray));
        System.out.println("整数数组: " + Arrays.toString(intArray));
        
        // 获取数组元素类型
        System.out.println("字符串数组元素类型: " + getComponentType(stringArray));
        System.out.println("整数数组元素类型: " + getComponentType(intArray));
    }
}

4.6.2 泛型与注解

泛型注解的定义和使用

java

import java.lang.annotation.*;
import java.lang.reflect.*;

// 1. 泛型注解定义
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface GenericAnnotation<T> {
    // 注解属性不能使用泛型类型参数
    // T value();  // 错误:注解属性不能是泛型
    
    Class<T> type();  // 但可以使用Class<T>
    String description() default "";
}

// 2. 使用泛型注解
@GenericAnnotation(type = String.class, description = "字符串处理器")
class StringProcessor {
    public String process(String input) {
        return input.toUpperCase();
    }
}

@GenericAnnotation(type = Integer.class)
class IntegerProcessor {
    public Integer process(Integer input) {
        return input * 2;
    }
}

// 3. 处理泛型注解
public class GenericAnnotationProcessor {
    public static void main(String[] args) {
        processAnnotation(StringProcessor.class);
        processAnnotation(IntegerProcessor.class);
    }
    
    public static void processAnnotation(Class<?> clazz) {
        GenericAnnotation annotation = clazz.getAnnotation(GenericAnnotation.class);
        if (annotation != null) {
            System.out.println("类: " + clazz.getSimpleName());
            System.out.println("注解类型: " + annotation.type());
            System.out.println("描述: " + annotation.description());
            System.out.println();
        }
    }
}

// 4. 注解与泛型方法的结合
class Validator {
    
    @SuppressWarnings("unchecked")
    public static <T> T validateAndCast(Object obj, Class<T> expectedType) {
        if (obj == null) {
            throw new IllegalArgumentException("对象不能为null");
        }
        
        if (!expectedType.isInstance(obj)) {
            throw new ClassCastException(
                String.format("对象类型不匹配: 期望%s, 实际%s",
                    expectedType.getName(), obj.getClass().getName()));
        }
        
        return (T) obj;
    }
    
    // 使用注解增强泛型方法
    @Deprecated
    public static <T> T oldValidate(Object obj, Class<T> type) {
        // 旧版本的实现
        return validateAndCast(obj, type);
    }
}

4.6.3 泛型与继承

泛型继承的几种形式

java

// 1. 泛型类继承泛型类
class BaseClass<T> {
    protected T value;
    
    public BaseClass(T value) {
        this.value = value;
    }
    
    public T getValue() {
        return value;
    }
}

// 子类保持泛型
class SubClass<T> extends BaseClass<T> {
    public SubClass(T value) {
        super(value);
    }
    
    // 可以添加额外的方法
    public void process() {
        System.out.println("处理: " + value);
    }
}

// 子类指定具体类型
class StringClass extends BaseClass<String> {
    public StringClass(String value) {
        super(value);
    }
    
    // 可以添加字符串特有的方法
    public int getLength() {
        return value.length();
    }
}

// 2. 泛型接口的实现
interface Processor<T> {
    T process(T input);
}

// 实现时指定具体类型
class StringProcessorImpl implements Processor<String> {
    @Override
    public String process(String input) {
        return input.toUpperCase();
    }
}

// 实现时保持泛型
class GenericProcessor<T> implements Processor<T> {
    @Override
    public T process(T input) {
        // 通用处理逻辑
        return input;
    }
}

// 3. 复杂的继承关系
interface Reader<T> {
    T read();
}

interface Writer<T> {
    void write(T data);
}

// 实现多个泛型接口
class FileHandler<T> implements Reader<T>, Writer<T> {
    @Override
    public T read() {
        // 实现读取逻辑
        return null;
    }
    
    @Override
    public void write(T data) {
        // 实现写入逻辑
    }
}

// 4. 泛型方法重写
class Base {
    public <T> T genericMethod(T input) {
        System.out.println("Base: " + input);
        return input;
    }
}

class Derived extends Base {
    // 重写泛型方法
    @Override
    public <T> T genericMethod(T input) {
        System.out.println("Derived: " + input);
        return super.genericMethod(input);
    }
}

// 5. 测试泛型继承
public class GenericInheritanceTest {
    public static void main(String[] args) {
        // 测试泛型类继承
        SubClass<String> sub = new SubClass<>("hello");
        System.out.println(sub.getValue());  // hello
        sub.process();  // 处理: hello
        
        StringClass strClass = new StringClass("world");
        System.out.println(strClass.getValue());  // world
        System.out.println(strClass.getLength());  // 5
        
        // 测试泛型接口实现
        Processor<String> processor = new StringProcessorImpl();
        String result = processor.process("hello");
        System.out.println(result);  // HELLO
    }
}

4.6.4 泛型的协变和逆变

协变和逆变概念

概念定义Java示例说明
协变子类型关系保持String[]Object[]的子类型数组是协变的
逆变子类型关系反转Comparator<Object>Comparator<String>的子类型?需要通配符
不变无子类型关系List<String>不是List<Object>的子类型泛型默认不变

数组协变的问题

java

// 数组是协变的 - 可能引发问题
Object[] objectArray = new String[10];  // 允许:String[]是Object[]的子类型
objectArray[0] = "hello";  // 正确
// objectArray[1] = 123;  // 运行时抛出ArrayStoreException

// 泛型是不变的 - 更安全
// List<Object> objectList = new ArrayList<String>();  // 编译错误
// List<String>不是List<Object>的子类型

使用通配符实现协变和逆变

java

import java.util.*;

// 1. 协变:使用上界通配符(? extends)
public class CovariantExample {
    
    // 协变读取
    public static double sum(List<? extends Number> numbers) {
        double total = 0;
        for (Number n : numbers) {
            total += n.doubleValue();
        }
        return total;
    }
    
    public static void main(String[] args) {
        List<Integer> integers = Arrays.asList(1, 2, 3);
        List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3);
        
        System.out.println("整数和: " + sum(integers));  // 6.0
        System.out.println("双精度和: " + sum(doubles));  // 6.6
        
        // 协变赋值
        List<? extends Number> numbers;
        numbers = integers;  // 协变:List<Integer>可以赋值给List<? extends Number>
        numbers = doubles;   // 协变:List<Double>也可以
    }
}

// 2. 逆变:使用下界通配符(? super)
public class ContravariantExample {
    
    // 逆变写入
    public static void addNumbers(List<? super Integer> list) {
        for (int i = 1; i <= 3; i++) {
            list.add(i);
        }
    }
    
    public static void main(String[] args) {
        List<Number> numbers = new ArrayList<>();
        List<Object> objects = new ArrayList<>();
        
        addNumbers(numbers);  // 逆变:可以添加Integer到Number列表
        addNumbers(objects);  // 逆变:可以添加Integer到Object列表
        
        System.out.println("numbers: " + numbers);  // [1, 2, 3]
        System.out.println("objects: " + objects);  // [1, 2, 3]
        
        // 逆变赋值
        List<? super Integer> intSuper;
        intSuper = numbers;  // 逆变:List<Number>可以赋值给List<? super Integer>
        intSuper = objects;  // 逆变:List<Object>也可以
    }
}

// 3. PECS原则的实际应用
public class PecsExample {
    
    // 生产者使用extends(协变)
    public static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp) {
        T candidate = null;
        for (T element : coll) {
            if (candidate == null || comp.compare(element, candidate) > 0) {
                candidate = element;
            }
        }
        return candidate;
    }
    
    // 消费者使用super(逆变)
    public static <T> void copy(List<? super T> dest, List<? extends T> src) {
        for (T element : src) {
            dest.add(element);
        }
    }
    
    public static void main(String[] args) {
        // 协变示例
        List<Integer> integers = Arrays.asList(3, 1, 4, 1, 5);
        
        // Comparator<Number>可以比较Integer(逆变)
        Comparator<Number> numberComparator = Comparator.comparing(Number::intValue);
        Integer maxInt = max(integers, numberComparator);
        System.out.println("最大值: " + maxInt);  // 5
        
        // 逆变示例
        List<Number> numbers = new ArrayList<>();
        copy(numbers, integers);  // 复制Integer到Number列表
        System.out.println("复制后: " + numbers);  // [3, 1, 4, 1, 5]
    }
}

// 4. 类型系统的层次结构
public class TypeHierarchy {
    
    static class Animal {}
    static class Mammal extends Animal {}
    static class Dog extends Mammal {}
    static class Cat extends Mammal {}
    
    public static void main(String[] args) {
        // 数组协变
        Mammal[] mammals = new Dog[5];  // 允许,但危险
        // mammals[0] = new Cat();  // 运行时错误:ArrayStoreException
        
        // 泛型不变(默认)
        // List<Mammal> mammalList = new ArrayList<Dog>();  // 编译错误
        
        // 使用通配符实现协变
        List<? extends Mammal> mammalsCovariant = new ArrayList<Dog>();  // 允许
        
        // 使用通配符实现逆变
        List<? super Dog> dogsContravariant = new ArrayList<Animal>();  // 允许
        
        // 复杂示例
        Comparator<Animal> animalComparator = Comparator.comparing(Object::toString);
        Comparator<Dog> dogComparator = animalComparator;  // 逆变:Comparator<Animal>是Comparator<Dog>的子类型
        
        List<Dog> dogs = Arrays.asList(new Dog(), new Dog());
        dogs.sort(dogComparator);  // 可以用Animal比较器排序Dog列表
    }
}

协变逆变总结

特性协变逆变不变
定义子类型关系保持子类型关系反转无子类型关系
Java数组支持不支持-
Java泛型通过? extends实现通过? super实现默认行为
读取安全不安全安全
写入不安全安全安全
PECSProducer ExtendsConsumer Super既生产又消费

实际应用场景

  • 协变:只读集合,如List<? extends Number>
  • 逆变:只写集合,如List<? super Integer>
  • 不变:需要同时读写,如List<T>

本章总结

  1. 泛型本质:参数化类型,编译时类型检查
  2. 类型擦除:为实现兼容性,运行时类型信息丢失
  3. 通配符:增加灵活性,? extends用于读取,? super用于写入
  4. PECS原则:指导通配符使用的最佳实践
  5. 反射与泛型:通过ParameterizedType获取泛型信息
  6. 协变逆变:理解类型系统的灵活性
  7. 最佳实践:合理使用泛型,平衡类型安全和代码灵活性

第五章:反射

5.1 什么是反射机制?反射机制的应用场景?

5.1.1 反射的基本概念和原理

反射是Java语言提供的一种在运行时动态获取和操作类、对象、方法、字段等信息的能力。

核心原理

  • Java程序运行时,每个类都有一个对应的Class对象
  • Class对象包含了该类的所有结构信息(字段、方法、构造器等)
  • 通过Class对象,可以在运行时动态操作该类的实例

反射的主要类

  • Class<T>:代表类的类型信息
  • Field:代表类的字段
  • Method:代表类的方法
  • Constructor<T>:代表类的构造方法

java

// 正常方式创建对象和调用方法
String str = "Hello";
int length = str.length();

// 反射方式
Class<?> clazz = String.class;
Method lengthMethod = clazz.getMethod("length");
Object result = lengthMethod.invoke(str);  // 返回5

5.1.2 反射的优缺点

优点

  1. 动态性:运行时获取类信息,动态创建对象、调用方法
  2. 灵活性:可以访问私有成员,突破封装限制
  3. 通用性:编写通用框架,如Spring、MyBatis等
  4. 扩展性:支持插件化架构和热部署

缺点

  1. 性能低:比直接调用慢很多
  2. 安全隐患:可以访问私有数据,破坏封装
  3. 代码复杂:可读性差,调试困难
  4. 类型安全:编译时无法检查类型错误

对比表

方面直接调用反射调用
性能快(纳秒级)慢(微秒级)
安全性受访问控制保护可绕过访问控制
代码可读性
编译时检查
灵活性固定高度灵活

5.1.3 反射的适用场景

适用场景

  1. 框架开发:Spring IoC容器、MyBatis ORM框架
  2. 动态代理:AOP编程、RPC框架
  3. 注解处理:JUnit测试、Spring MVC
  4. 工具类库:JSON序列化(Jackson)、XML解析
  5. IDE插件:代码提示、自动补全

不适用场景

  1. 性能敏感:核心业务逻辑、高频调用
  2. 简单需求:可以直接通过API实现的功能
  3. 需要严格封装:安全性要求高的组件

5.1.4 反射的安全性问题

安全问题

  1. 破坏封装:可以访问和修改私有字段
  2. 执行任意代码:通过反射调用任意方法
  3. 绕过安全检查:使用setAccessible(true)禁用访问检查

安全防护

java

// 使用安全管理器限制反射
SecurityManager securityManager = new SecurityManager() {
    @Override
    public void checkPermission(Permission perm) {
        if (perm instanceof ReflectPermission && "suppressAccessChecks".equals(perm.getName())) {
            throw new SecurityException("禁止反射访问私有成员!");
        }
    }
};
System.setSecurityManager(securityManager);

5.1.5 反射的性能问题

性能差的原因

  1. 动态解析:运行时解析类信息
  2. 安全检查:每次调用都要检查访问权限
  3. 方法调用:使用JNI或动态字节码,无法被JIT优化
  4. 自动装箱:参数和返回值可能需要装箱拆箱

5.2 如何通过反射创建对象、调用方法、访问字段?

5.2.1 获取Class对象的3种方式

方式对比

方式语法适用场景性能异常处理
类名.classString.class编译时已知类名最好不需要
对象.getClass()obj.getClass()已有对象实例不需要
Class.forName()Class.forName("java.lang.String")动态加载类需要处理

示例

java

// 1. 类名.class
Class<String> stringClass = String.class;

// 2. 对象.getClass()
String str = "hello";
Class<?> clazz1 = str.getClass();

// 3. Class.forName()
Class<?> clazz2 = Class.forName("java.lang.String");

// 验证三种方式获取的是同一个Class对象
System.out.println(stringClass == clazz1);  // true
System.out.println(clazz1 == clazz2);        // true

5.2.2 反射创建对象(无参、有参构造)

创建对象方式

构造类型获取方法创建实例注意事项
无参构造getConstructor()newInstance()类必须有public无参构造
有参构造getConstructor(Class<?>...)newInstance(Object...)参数类型匹配
私有构造getDeclaredConstructor(Class<?>...)newInstance(Object...)需要setAccessible(true)

示例

java

class Person {
    private String name;
    private int age;
    
    public Person() {
        System.out.println("无参构造");
    }
    
    public Person(String name) {
        this.name = name;
        System.out.println("单参构造: " + name);
    }
    
    private Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("私有构造: " + name + ", " + age);
    }
}

// 使用反射创建对象
Class<Person> clazz = Person.class;

// 1. 无参构造
Constructor<Person> cons1 = clazz.getConstructor();
Person p1 = cons1.newInstance();

// 2. 有参构造
Constructor<Person> cons2 = clazz.getConstructor(String.class);
Person p2 = cons2.newInstance("张三");

// 3. 私有构造
Constructor<Person> cons3 = clazz.getDeclaredConstructor(String.class, int.class);
cons3.setAccessible(true);  // 关键:允许访问私有构造
Person p3 = cons3.newInstance("李四", 25);

5.2.3 反射调用方法(公有、私有、静态)

方法调用对比

方法类型获取方法调用方式注意事项
公有实例方法getMethod(name, paramTypes)invoke(obj, args)需要对象实例
私有实例方法getDeclaredMethod(name, paramTypes)invoke(obj, args)需要setAccessible(true)
静态方法getMethod(name, paramTypes)invoke(null, args)对象参数传null

示例

java

class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
    
    private int multiply(int a, int b) {
        return a * b;
    }
    
    public static int subtract(int a, int b) {
        return a - b;
    }
}

Calculator calc = new Calculator();
Class<?> clazz = calc.getClass();

// 1. 调用公有方法
Method addMethod = clazz.getMethod("add", int.class, int.class);
int result = (int) addMethod.invoke(calc, 10, 20);  // 30

// 2. 调用私有方法
Method multiplyMethod = clazz.getDeclaredMethod("multiply", int.class, int.class);
multiplyMethod.setAccessible(true);
int product = (int) multiplyMethod.invoke(calc, 10, 20);  // 200

// 3. 调用静态方法
Method subtractMethod = clazz.getMethod("subtract", int.class, int.class);
int difference = (int) subtractMethod.invoke(null, 20, 10);  // 10

5.2.4 反射访问字段(获取、设置值)

字段访问对比

字段类型获取方法读写操作注意事项
公有字段getField(name)get(obj)set(obj, value)直接访问
私有字段getDeclaredField(name)get(obj)set(obj, value)需要setAccessible(true)
静态字段getField(name)get(null)set(null, value)对象参数传null

示例

java

class Student {
    public String name;
    private int age;
    public static String school = "清华大学";
}

Student stu = new Student();
stu.name = "张三";
Class<?> clazz = stu.getClass();

// 1. 访问公有字段
Field nameField = clazz.getField("name");
System.out.println(nameField.get(stu));  // 张三
nameField.set(stu, "李四");
System.out.println(stu.name);  // 李四

// 2. 访问私有字段
Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(stu, 25);
System.out.println(ageField.get(stu));  // 25

// 3. 访问静态字段
Field schoolField = clazz.getField("school");
System.out.println(schoolField.get(null));  // 清华大学
schoolField.set(null, "北京大学");
System.out.println(Student.school);  // 北京大学

5.2.5 反射操作数组

数组操作API

操作方法说明
创建数组Array.newInstance(componentType, length)动态创建数组
获取元素Array.get(array, index)获取指定位置元素
设置元素Array.set(array, index, value)设置指定位置元素
获取长度Array.getLength(array)获取数组长度

示例

java

// 创建一维数组
String[] stringArray = (String[]) Array.newInstance(String.class, 5);
Array.set(stringArray, 0, "Java");
Array.set(stringArray, 1, "Python");
System.out.println(Array.get(stringArray, 0));  // Java
System.out.println(Array.getLength(stringArray));  // 5

// 创建二维数组
int[][] matrix = (int[][]) Array.newInstance(int.class, 3, 4);
int[] row = (int[]) Array.get(matrix, 0);
Array.set(row, 0, 100);
System.out.println(Array.get(row, 0));  // 100

5.2.6 反射操作注解

注解操作API

操作方法说明
获取类注解getAnnotation(Class)获取指定类型的注解
获取所有注解getAnnotations()获取所有注解
检查注解存在isAnnotationPresent(Class)检查是否有指定注解

示例

java

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface MyAnnotation {
    String value();
}

@MyAnnotation("测试类")
class MyClass {
    @Deprecated
    public void oldMethod() {}
}

Class<MyClass> clazz = MyClass.class;

// 1. 获取类上的注解
MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class);
if (annotation != null) {
    System.out.println(annotation.value());  // 测试类
}

// 2. 检查注解存在
boolean hasAnnotation = clazz.isAnnotationPresent(MyAnnotation.class);
System.out.println(hasAnnotation);  // true

// 3. 获取方法上的注解
Method method = clazz.getMethod("oldMethod");
Deprecated deprecated = method.getAnnotation(Deprecated.class);
System.out.println(deprecated != null);  // true

5.3 为什么反射性能较差?如何优化?

5.3.1 反射性能差的原因

主要原因

  1. 动态解析:运行时解析类信息,需要查找和验证
  2. 安全检查:每次调用都要检查访问权限
  3. 方法调用开销:需要通过JNI或动态字节码,无法被JIT优化
  4. 自动装箱:基本类型需要装箱为包装类型

性能对比测试

java

class TestClass {
    public int add(int a, int b) { return a + b; }
}

// 直接调用
long start = System.nanoTime();
for (int i = 0; i < 1000000; i++) {
    test.add(i, i);
}
long directTime = System.nanoTime() - start;

// 反射调用(无优化)
start = System.nanoTime();
for (int i = 0; i < 1000000; i++) {
    Method method = TestClass.class.getMethod("add", int.class, int.class);
    method.invoke(test, i, i);
}
long reflectionTime = System.nanoTime() - start;

System.out.println("直接调用: " + directTime + " ns");
System.out.println("反射调用: " + reflectionTime + " ns");
System.out.println("反射比直接调用慢" + (reflectionTime/directTime) + "倍");

5.3.2 反射性能优化方法

优化策略对比

优化方法原理效果实现难度
缓存反射对象避免重复获取Class、Method等对象显著提升简单
setAccessible禁用访问检查中等提升简单
避免自动装箱使用基本类型参数轻微提升中等
MethodHandle使用invokedynamic指令大幅提升中等
字节码增强生成直接调用代码最佳效果困难

5.3.3 使用setAccessible优化

原理setAccessible(true)禁用安全检查,提高后续访问性能

java

class Target {
    private int value;
    
    private int getValue() { return value; }
}

Target target = new Target();
Class<?> clazz = target.getClass();

// 未优化
Field field = clazz.getDeclaredField("value");
Method method = clazz.getDeclaredMethod("getValue");

long start = System.nanoTime();
for (int i = 0; i < 100000; i++) {
    field.setAccessible(true);  // 每次都设置
    field.set(target, i);
    method.setAccessible(true);  // 每次都设置
    method.invoke(target);
}
long time1 = System.nanoTime() - start;

// 优化后
field.setAccessible(true);  // 一次性设置
method.setAccessible(true);  // 一次性设置
start = System.nanoTime();
for (int i = 0; i < 100000; i++) {
    field.set(target, i);
    method.invoke(target);
}
long time2 = System.nanoTime() - start;

System.out.println("优化前: " + time1 + " ns");
System.out.println("优化后: " + time2 + " ns");
System.out.println("提升: " + (time1 - time2) + " ns");

5.3.4 缓存反射结果

缓存策略:将反射获取的Class、Method、Field等对象缓存起来,避免重复获取

java

import java.util.concurrent.ConcurrentHashMap;

class ReflectionCache {
    private static final ConcurrentHashMap<String, Class<?>> classCache = new ConcurrentHashMap<>();
    private static final ConcurrentHashMap<String, Method> methodCache = new ConcurrentHashMap<>();
    
    public static Class<?> getClass(String className) throws Exception {
        return classCache.computeIfAbsent(className, key -> {
            try {
                return Class.forName(key);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        });
    }
    
    public static Method getMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) 
            throws Exception {
        String key = clazz.getName() + "#" + methodName;
        for (int i = 0; paramTypes != null && i < paramTypes.length; i++) {
            key += "#" + paramTypes[i].getName();
        }
        
        return methodCache.computeIfAbsent(key, k -> {
            try {
                Method method = clazz.getMethod(methodName, paramTypes);
                method.setAccessible(true);  // 一并优化
                return method;
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        });
    }
}

5.3.5 MethodHandle的替代方案

MethodHandle vs Reflection

特性MethodHandleReflection
性能接近直接调用较慢
类型安全强类型检查运行时检查
异常处理不抛出检查异常抛出检查异常
访问控制遵守访问权限可突破访问权限

示例

java

import java.lang.invoke.*;

class Calculator {
    public int add(int a, int b) { return a + b; }
}

Calculator calc = new Calculator();

// 反射方式
Method method = Calculator.class.getMethod("add", int.class, int.class);
long start = System.nanoTime();
for (int i = 0; i < 1000000; i++) {
    method.invoke(calc, i, i);
}
long reflectionTime = System.nanoTime() - start;

// MethodHandle方式
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle handle = lookup.findVirtual(Calculator.class, "add", 
    MethodType.methodType(int.class, int.class, int.class));
start = System.nanoTime();
for (int i = 0; i < 1000000; i++) {
    handle.invoke(calc, i, i);
}
long handleTime = System.nanoTime() - start;

System.out.println("反射: " + reflectionTime + " ns");
System.out.println("MethodHandle: " + handleTime + " ns");
System.out.println("MethodHandle快" + (reflectionTime/handleTime) + "倍");

5.3.6 字节码增强技术

技术对比

技术原理性能使用难度代表框架
ASM直接操作字节码最佳困难Spring、MyBatis
Javassist源代码级别操作中等简单Hibernate
Byte Buddy流畅API封装良好中等Mockito
CGLIB基于ASM的封装良好中等Spring AOP

5.4 反射的高级应用

5.4.1 动态代理的实现

动态代理原理

  • 在运行时动态创建代理类和对象
  • 代理类实现指定接口
  • 方法调用转发到InvocationHandler

示例

java

interface UserService {
    void addUser(String name);
    void deleteUser(String name);
}

class UserServiceImpl implements UserService {
    public void addUser(String name) {
        System.out.println("添加用户: " + name);
    }
    
    public void deleteUser(String name) {
        System.out.println("删除用户: " + name);
    }
}

class LoggingHandler implements InvocationHandler {
    private final Object target;
    
    public LoggingHandler(Object target) {
        this.target = target;
    }
    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("【开始执行】" + method.getName());
        long start = System.currentTimeMillis();
        
        Object result = method.invoke(target, args);
        
        long end = System.currentTimeMillis();
        System.out.println("【执行完成】耗时: " + (end - start) + "ms");
        return result;
    }
}

// 使用
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    new LoggingHandler(target)
);

proxy.addUser("张三");
proxy.deleteUser("李四");

5.4.2 注解处理器

运行时注解处理

java

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Test {
    boolean enabled() default true;
    String description() default "";
}

class TestClass {
    @Test(description = "测试方法1")
    public void testMethod1() {
        System.out.println("执行testMethod1");
    }
    
    @Test(enabled = false)
    public void testMethod2() {
        System.out.println("执行testMethod2");
    }
}

class TestRunner {
    public static void runTests(Object obj) throws Exception {
        Class<?> clazz = obj.getClass();
        
        for (Method method : clazz.getDeclaredMethods()) {
            if (method.isAnnotationPresent(Test.class)) {
                Test annotation = method.getAnnotation(Test.class);
                
                if (annotation.enabled()) {
                    System.out.println("执行测试: " + annotation.description());
                    method.invoke(obj);
                } else {
                    System.out.println("跳过测试: " + method.getName());
                }
            }
        }
    }
}

// 使用
TestRunner.runTests(new TestClass());

5.4.3 反射与泛型的结合

泛型反射:获取泛型类型信息

java

import java.lang.reflect.*;

class GenericClass<T> {
    private T value;
    
    public List<T> getList() {
        return new ArrayList<>();
    }
}

class StringClass extends GenericClass<String> {
}

// 获取泛型信息
public class GenericReflectionDemo {
    public static void main(String[] args) throws Exception {
        // 获取泛型父类的类型参数
        Type genericSuperclass = StringClass.class.getGenericSuperclass();
        
        if (genericSuperclass instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) genericSuperclass;
            Type[] typeArgs = pt.getActualTypeArguments();
            System.out.println("泛型参数: " + typeArgs[0]);  // class java.lang.String
        }
        
        // 获取泛型方法的返回类型
        Method method = GenericClass.class.getMethod("getList");
        Type returnType = method.getGenericReturnType();
        
        if (returnType instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) returnType;
            System.out.println("返回类型: " + pt.getRawType());  // interface java.util.List
            System.out.println("泛型参数: " + pt.getActualTypeArguments()[0]);  // T
        }
    }
}

5.4.4 反射的安全管理

安全管理策略

策略实现方式效果适用场景
SecurityManager安全管理器全面控制服务器应用
AccessController访问控制器精细控制特权代码
自定义ClassLoader类加载器隔离沙箱环境插件系统

示例

java

class SecureClass {
    private String secret = "机密信息";
}

// 启用安全管理器
SecurityManager sm = new SecurityManager() {
    @Override
    public void checkPermission(Permission perm) {
        if (perm instanceof ReflectPermission && "suppressAccessChecks".equals(perm.getName())) {
            throw new SecurityException("禁止反射访问私有成员!");
        }
    }
};
System.setSecurityManager(sm);

try {
    SecureClass obj = new SecureClass();
    Field field = SecureClass.class.getDeclaredField("secret");
    field.setAccessible(true);  // 抛出SecurityException
} catch (SecurityException e) {
    System.out.println("安全异常: " + e.getMessage());
}

// 使用特权代码
String result = AccessController.doPrivileged(new PrivilegedAction<String>() {
    public String run() {
        try {
            SecureClass obj = new SecureClass();
            Field field = SecureClass.class.getDeclaredField("secret");
            field.setAccessible(true);
            return (String) field.get(obj);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
});
System.out.println("特权代码结果: " + result);

总结
反射是Java强大的动态特性,但需要谨慎使用:

  1. 优先使用直接调用,避免不必要的反射
  2. 缓存反射对象,提高性能
  3. 注意安全问题,避免恶意代码利用反射
  4. 合理使用动态代理,实现AOP等高级特性

第六章:注解

6.1 注解的定义和分类

6.1.1 注解的基本概念

注解(Annotation)  是Java 5引入的一种元数据机制,用于为程序元素(类、方法、字段等)添加额外的信息和标记。

注解的核心特性

  1. 元数据:为代码提供附加信息,不影响程序逻辑
  2. 标记机制:标识程序元素的特性或行为
  3. 可处理性:可通过编译器和反射进行解析处理

注解的基本语法

java

// 定义注解
public @interface MyAnnotation {
    String value();
}

// 使用注解
@MyAnnotation("测试")
public class TestClass {
}

6.1.2 元注解

元注解:用于定义注解的注解,Java提供5种标准元注解。

元注解对比表

元注解作用参数示例说明
@Target指定注解可应用的位置ElementType枚举@Target(ElementType.METHOD)控制注解使用范围
@Retention指定注解保留时间RetentionPolicy枚举@Retention(RetentionPolicy.RUNTIME)控制注解生命周期
@Documented包含在Javadoc中@Documented生成API文档时包含
@Inherited子类继承父类注解@Inherited仅对类注解有效
@Repeatable可重复使用容器注解类@Repeatable(Schedules.class)Java 8新增

示例详解

java

import java.lang.annotation.*;

// 1. @Target - 指定注解使用位置
@Target({
    ElementType.TYPE,       // 类、接口、枚举
    ElementType.FIELD,      // 字段
    ElementType.METHOD,     // 方法
    ElementType.PARAMETER,  // 参数
    ElementType.CONSTRUCTOR,// 构造方法
    ElementType.LOCAL_VARIABLE, // 局部变量
    ElementType.ANNOTATION_TYPE, // 注解类型
    ElementType.PACKAGE,    // 包
    ElementType.TYPE_PARAMETER, // 类型参数(Java 8)
    ElementType.TYPE_USE    // 类型使用(Java 8)
})
@interface MyTargetAnnotation {
}

// 2. @Retention - 指定注解生命周期
@Retention(RetentionPolicy.SOURCE)   // 源码级别,编译后丢弃
@interface SourceAnnotation {}

@Retention(RetentionPolicy.CLASS)    // 类文件级别,运行时不保留
@interface ClassAnnotation {}

@Retention(RetentionPolicy.RUNTIME)  // 运行时保留,可通过反射获取
@interface RuntimeAnnotation {}

// 3. @Documented - 包含在Javadoc
@Documented
@interface DocumentedAnnotation {
    String value();
}

// 4. @Inherited - 子类继承
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface InheritedAnnotation {
    String value();
}

@InheritedAnnotation("父类")
class Parent {}

class Child extends Parent {}  // Child也会拥有@InheritedAnnotation

// 5. @Repeatable - 可重复注解(Java 8)
@Repeatable(Schedules.class)
@Retention(RetentionPolicy.RUNTIME)
@interface Schedule {
    String time();
}

@Retention(RetentionPolicy.RUNTIME)
@interface Schedules {
    Schedule[] value();  // 容器注解
}

// 使用可重复注解
@Schedule(time = "9:00")
@Schedule(time = "15:00")
class Meeting {}

6.1.3 内置注解

Java内置注解分类

分类注解作用示例
编译检查@Override检查方法重写@Override public String toString()
@Deprecated标记过时元素@Deprecated public void oldMethod()
@SuppressWarnings抑制编译器警告@SuppressWarnings("unchecked")
@SafeVarargs泛型参数安全@SafeVarargs final void method(T... args)
@FunctionalInterface函数式接口检查@FunctionalInterface interface Converter
资源管理@PostConstruct初始化后执行@PostConstruct void init()
@PreDestroy销毁前执行@PreDestroy void cleanup()
模块系统@Deprecated(forRemoval)计划移除@Deprecated(forRemoval=true)
@SuppressWarnings("removal")抑制移除警告@SuppressWarnings("removal")

内置注解使用示例

java

// 1. @Override - 方法重写检查
class Parent {
    public void show() {
        System.out.println("Parent");
    }
}

class Child extends Parent {
    @Override  // 检查是否正确重写,防止拼写错误
    public void show() {
        System.out.println("Child");
    }
    
    // 错误示例:如果没有重写父类方法,编译错误
    // @Override
    // public void display() {}  // 编译错误:方法不会重写或实现超类型的方法
}

// 2. @Deprecated - 标记过时
class OldAPI {
    /**
     * 使用新的newMethod代替
     * @deprecated 从2.0版本开始过时,请使用newMethod
     */
    @Deprecated(since = "2.0", forRemoval = false)
    public void oldMethod() {
        System.out.println("过时方法");
    }
    
    public void newMethod() {
        System.out.println("新方法");
    }
}

// 3. @SuppressWarnings - 抑制警告
class WarningExample {
    @SuppressWarnings("unchecked")  // 抑制未检查转换警告
    public List<String> getList() {
        return new ArrayList();  // 未指定泛型类型
    }
    
    @SuppressWarnings({"rawtypes", "unused"})  // 抑制多个警告
    public void test() {
        List list = new ArrayList();  // 使用原始类型
        int unusedVar = 10;  // 未使用的变量
    }
}

// 4. @SafeVarargs - 安全泛型参数
class VarargsExample {
    @SafeVarargs  // 保证泛型可变参数的安全性
    public final <T> List<T> asList(T... args) {
        List<T> list = new ArrayList<>();
        for (T arg : args) {
            list.add(arg);
        }
        return list;
    }
}

// 5. @FunctionalInterface - 函数式接口
@FunctionalInterface  // 确保接口只有一个抽象方法
interface Converter<F, T> {
    T convert(F from);
    
    // 可以有默认方法和静态方法
    default Converter<F, T> andThen(Converter<T, ?> after) {
        return (F f) -> after.convert(convert(f));
    }
    
    static <T> Converter<T, T> identity() {
        return t -> t;
    }
}

6.1.4 注解的分类

按功能分类

分类特点示例使用场景
标记注解没有元素的注解@Override@Deprecated标记特定状态
单值注解只有一个value元素@SuppressWarnings("value")简单配置
完整注解有多个元素@RequestMapping(method=GET)复杂配置
元注解定义注解的注解@Target@Retention注解定义
组合注解包含其他注解Spring的@SpringBootApplication简化配置

示例

java

// 1. 标记注解(无元素)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface MarkerAnnotation {
    // 没有元素,仅用于标记
}

// 2. 单值注解(只有value元素)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface SingleValueAnnotation {
    String value();  // 只有value元素,使用时可以省略"value="
    
    // 使用示例:
    // @SingleValueAnnotation("test")
    // @SingleValueAnnotation(value = "test")  // 等价
}

// 3. 完整注解(多个元素)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface FullAnnotation {
    String name();
    int priority() default 1;
    String[] tags() default {};
    Class<?> type() default Object.class;
}

// 4. 组合注解示例
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@MarkerAnnotation
@interface CombinedAnnotation {
    SingleValueAnnotation config() default @SingleValueAnnotation("default");
    FullAnnotation[] details() default {};
}

6.2 自定义注解的实现

6.2.1 定义注解的语法

注解定义规则

  1. 使用@interface关键字
  2. 可以包含元素(类似方法声明)
  3. 元素可以有默认值
  4. 可以添加元注解

基本语法

java

[访问修饰符] @interface 注解名 {
    类型 元素名() [default 默认值];
    类型 元素名() [default 默认值];
    // ...
}

示例

java

// 简单注解定义
public @interface Author {
    String name();
    String email() default "";
    String date();
}

// 使用示例
@Author(
    name = "张三",
    email = "zhangsan@example.com",
    date = "2024-01-15"
)
public class MyClass {
    @Author(name = "李四", date = "2024-01-16")
    public void method() {
    }
}

6.2.2 注解元素的类型限制

允许的元素类型

类型示例说明
基本类型int value()byte、short、int、long、float、double、char、boolean
StringString name()字符串类型
ClassClass<?> type()类类型
EnumStatus status()枚举类型
AnnotationAuthor author()注解类型
数组String[] tags()以上类型的数组

限制

  1. 不能使用null作为默认值
  2. 不能是泛型类型
  3. 不能是void类型

类型示例

java

// 枚举定义
enum Status {
    ACTIVE, INACTIVE, PENDING
}

// 各种类型的注解元素
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TypeExample {
    // 基本类型
    int id();
    double score() default 0.0;
    boolean enabled() default true;
    
    // String类型
    String name();
    
    // Class类型
    Class<?> targetClass();
    
    // 枚举类型
    Status status();
    
    // 注解类型
    Author author();
    
    // 数组类型
    String[] tags();
    int[] numbers() default {1, 2, 3};
    Class<?>[] classes() default {};
    
    // 默认值不能是null
    // String description() default null;  // 编译错误
}

// 使用示例
@TypeExample(
    id = 1,
    name = "测试",
    targetClass = String.class,
    status = Status.ACTIVE,
    author = @Author(name = "王五", date = "2024-01-17"),
    tags = {"java", "annotation"}
)
class ExampleClass {
}

6.2.3 注解的默认值

默认值规则

  1. 使用default关键字指定默认值
  2. 默认值必须是编译时常量
  3. 数组默认值使用{}表示空数组

示例

java

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface DefaultValueExample {
    // 各种类型的默认值
    int count() default 0;
    String name() default "default";
    boolean enabled() default true;
    Class<?> type() default Object.class;
    Status status() default Status.PENDING;
    String[] tags() default {};
    int[] values() default {1, 2, 3};
    
    // 注解元素的默认值
    Author author() default @Author(name = "默认作者", date = "2024-01-01");
}

// 使用默认值
class DefaultValueUsage {
    @DefaultValueExample  // 使用所有默认值
    public void method1() {}
    
    @DefaultValueExample(
        name = "自定义",
        count = 10,
        tags = {"tag1", "tag2"}
    )
    public void method2() {}
}

6.2.4 注解的继承性

继承规则

  1. 默认情况下,注解不会被继承
  2. 使用@Inherited元注解的类级别注解可以被继承
  3. 方法、字段等元素上的注解不会被继承

继承示例

java

// 可继承的注解
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface InheritableAnnotation {
    String value();
}

// 不可继承的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface NonInheritableAnnotation {
    String value();
}

// 父类
@InheritableAnnotation("父类的可继承注解")
@NonInheritableAnnotation("父类的不可继承注解")
class Parent {
    @InheritableAnnotation("方法上的注解")  // 不会被子类继承
    public void method() {}
}

// 子类
class Child extends Parent {
    // 自动继承@InheritableAnnotation,但不继承@NonInheritableAnnotation
    // 也不会继承方法上的@InheritableAnnotation
}

// 测试继承性
public class InheritanceTest {
    public static void main(String[] args) {
        Class<Child> childClass = Child.class;
        
        // 检查类上的注解
        System.out.println("Child是否有@InheritableAnnotation: " + 
            childClass.isAnnotationPresent(InheritableAnnotation.class));  // true
        
        System.out.println("Child是否有@NonInheritableAnnotation: " + 
            childClass.isAnnotationPresent(NonInheritableAnnotation.class));  // false
        
        // 检查父类方法上的注解是否被继承
        try {
            Method method = Parent.class.getMethod("method");
            System.out.println("Parent.method是否有@InheritableAnnotation: " + 
                method.isAnnotationPresent(InheritableAnnotation.class));  // true
            
            Method childMethod = Child.class.getMethod("method");
            System.out.println("Child.method是否有@InheritableAnnotation: " + 
                childMethod.isAnnotationPresent(InheritableAnnotation.class));  // false
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

6.3 注解处理器

6.3.1 编译时注解处理器

编译时处理:在编译期间处理注解,生成额外的代码或进行编译检查。

特点

  • 处理时机:Java编译期间
  • 技术:APT(Annotation Processing Tool)
  • 输出:生成新的Java源文件
  • 应用:Lombok、Dagger、MapStruct等

工作流程

text

源代码 → 编译器 → 注解处理器 → 生成新文件 → 编译器 → 字节码

6.3.2 运行时注解处理器

运行时处理:在程序运行期间通过反射处理注解。

特点

  • 处理时机:程序运行期间
  • 技术:反射机制
  • 输出:影响程序运行行为
  • 应用:Spring框架、JUnit测试等

对比表

方面编译时处理运行时处理
处理时机编译期间运行期间
技术实现APT反射
性能影响编译时一次性处理运行时持续开销
灵活性有限,生成固定代码高,动态处理
典型应用代码生成工具框架配置处理
可见性需要RetentionPolicy.SOURCE需要RetentionPolicy.RUNTIME

6.3.3 APT(Annotation Processing Tool)

APT工作流程

  1. 编译器解析源代码,构建抽象语法树
  2. 调用注册的注解处理器
  3. 处理器处理注解元素
  4. 可生成新的源代码文件
  5. 编译器重新解析生成的文件

处理器注册
META-INF/services/javax.annotation.processing.Processor文件中注册处理器类。

6.3.4 注解处理器的编写

简单注解处理器示例

java

// 1. 定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)  // 源码级别,编译后丢弃
public @interface GenerateToString {
    boolean includeFieldNames() default true;
}

// 2. 注解处理器
import javax.annotation.processing.*;
import javax.lang.model.*;
import javax.lang.model.element.*;
import javax.tools.*;
import java.io.*;
import java.util.*;

@SupportedAnnotationTypes("GenerateToString")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class GenerateToStringProcessor extends AbstractProcessor {
    
    @Override
    public boolean process(Set<? extends TypeElement> annotations, 
                          RoundEnvironment roundEnv) {
        
        for (Element element : roundEnv.getElementsAnnotatedWith(GenerateToString.class)) {
            if (element.getKind() == ElementKind.CLASS) {
                TypeElement classElement = (TypeElement) element;
                GenerateToString annotation = classElement.getAnnotation(GenerateToString.class);
                generateToStringClass(classElement, annotation.includeFieldNames());
            }
        }
        return true;
    }
    
    private void generateToStringClass(TypeElement classElement, boolean includeFieldNames) {
        String className = classElement.getSimpleName().toString();
        String packageName = processingEnv.getElementUtils()
                                         .getPackageOf(classElement).toString();
        
        StringBuilder code = new StringBuilder();
        
        // 生成包声明
        if (!packageName.isEmpty()) {
            code.append("package ").append(packageName).append(";\n\n");
        }
        
        // 生成导入
        code.append("import java.lang.StringBuilder;\n\n");
        
        // 生成类定义
        code.append("public class ").append(className).append("ToStringHelper {\n\n");
        
        // 生成toString方法
        code.append("    public static String toString(").append(className).append(" obj) {\n");
        code.append("        StringBuilder sb = new StringBuilder();\n");
        code.append("        sb.append("").append(className).append("{");\n");
        
        // 遍历字段
        boolean firstField = true;
        for (Element member : processingEnv.getElementUtils()
                                           .getAllMembers(classElement)) {
            if (member.getKind() == ElementKind.FIELD) {
                VariableElement field = (VariableElement) member;
                String fieldName = field.getSimpleName().toString();
                
                if (!firstField) {
                    code.append("        sb.append(", ");\n");
                }
                
                if (includeFieldNames) {
                    code.append("        sb.append("").append(fieldName).append("=");\n");
                }
                
                code.append("        sb.append(obj.").append(fieldName).append(");\n");
                firstField = false;
            }
        }
        
        code.append("        sb.append("}");\n");
        code.append("        return sb.toString();\n");
        code.append("    }\n");
        code.append("}\n");
        
        // 写入文件
        try {
            JavaFileObject file = processingEnv.getFiler()
                    .createSourceFile(packageName + "." + className + "ToStringHelper");
            try (Writer writer = file.openWriter()) {
                writer.write(code.toString());
            }
        } catch (IOException e) {
            processingEnv.getMessager().printMessage(
                Diagnostic.Kind.ERROR, "生成文件失败: " + e.getMessage());
        }
    }
}

6.3.5 注解在AOP中的应用

AOP中的注解使用

java

// 1. 定义AOP注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecution {
    String value() default "";
    Level level() default Level.INFO;
    
    enum Level {
        DEBUG, INFO, WARN, ERROR
    }
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MeasureTime {
    String name() default "";
}

// 2. 业务类使用注解
class BusinessService {
    @LogExecution("执行用户查询")
    @MeasureTime(name = "queryUser")
    public User queryUser(String userId) {
        // 业务逻辑
        return new User(userId, "张三");
    }
    
    @LogExecution(value = "保存用户", level = LogExecution.Level.WARN)
    public void saveUser(User user) {
        // 业务逻辑
    }
}

// 3. AOP处理器
class AOPHandler {
    public static Object invokeWithAOP(Object target, Method method, Object[] args) 
            throws Throwable {
        
        long startTime = 0;
        
        // 处理@MeasureTime
        if (method.isAnnotationPresent(MeasureTime.class)) {
            startTime = System.currentTimeMillis();
        }
        
        // 处理@LogExecution
        if (method.isAnnotationPresent(LogExecution.class)) {
            LogExecution logAnnotation = method.getAnnotation(LogExecution.class);
            System.out.println("[" + logAnnotation.level() + "] " + 
                              logAnnotation.value() + " - 开始执行");
        }
        
        // 执行原方法
        Object result = method.invoke(target, args);
        
        // 执行后处理
        if (method.isAnnotationPresent(LogExecution.class)) {
            LogExecution logAnnotation = method.getAnnotation(LogExecution.class);
            System.out.println("[" + logAnnotation.level() + "] " + 
                              logAnnotation.value() + " - 执行完成");
        }
        
        if (method.isAnnotationPresent(MeasureTime.class)) {
            long endTime = System.currentTimeMillis();
            MeasureTime timeAnnotation = method.getAnnotation(MeasureTime.class);
            System.out.println("方法 " + timeAnnotation.name() + " 执行时间: " + 
                              (endTime - startTime) + "ms");
        }
        
        return result;
    }
}

// 4. 使用AOP
public class AOPExample {
    public static void main(String[] args) throws Exception {
        BusinessService service = new BusinessService();
        Method queryMethod = BusinessService.class.getMethod("queryUser", String.class);
        
        // 通过AOP调用
        User user = (User) AOPHandler.invokeWithAOP(service, queryMethod, 
                                                    new Object[]{"123"});
        System.out.println("查询结果: " + user);
    }
}

6.4 注解的实际应用

6.4.1 自定义注解的实际案例

案例1:数据验证注解

java

// 验证注解定义
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotNull {
    String message() default "字段不能为空";
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Size {
    int min() default 0;
    int max() default Integer.MAX_VALUE;
    String message() default "长度不符合要求";
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Email {
    String message() default "邮箱格式不正确";
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Pattern {
    String regexp();
    String message() default "格式不符合要求";
}

// 验证器
class Validator {
    public static List<String> validate(Object obj) {
        List<String> errors = new ArrayList<>();
        Class<?> clazz = obj.getClass();
        
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            
            try {
                Object value = field.get(obj);
                
                // 检查@NotNull
                if (field.isAnnotationPresent(NotNull.class)) {
                    NotNull annotation = field.getAnnotation(NotNull.class);
                    if (value == null || (value instanceof String && ((String) value).isEmpty())) {
                        errors.add(field.getName() + ": " + annotation.message());
                    }
                }
                
                // 检查@Size
                if (field.isAnnotationPresent(Size.class)) {
                    Size annotation = field.getAnnotation(Size.class);
                    if (value instanceof String) {
                        String str = (String) value;
                        if (str.length() < annotation.min() || str.length() > annotation.max()) {
                            errors.add(field.getName() + ": " + annotation.message());
                        }
                    }
                }
                
                // 检查@Email
                if (field.isAnnotationPresent(Email.class)) {
                    Email annotation = field.getAnnotation(Email.class);
                    if (value instanceof String) {
                        String email = (String) value;
                        if (!email.matches("^[A-Za-z0-9+_.-]+@(.+)$")) {
                            errors.add(field.getName() + ": " + annotation.message());
                        }
                    }
                }
                
                // 检查@Pattern
                if (field.isAnnotationPresent(Pattern.class)) {
                    Pattern annotation = field.getAnnotation(Pattern.class);
                    if (value instanceof String) {
                        String str = (String) value;
                        if (!str.matches(annotation.regexp())) {
                            errors.add(field.getName() + ": " + annotation.message());
                        }
                    }
                }
                
            } catch (IllegalAccessException e) {
                errors.add("无法访问字段: " + field.getName());
            }
        }
        
        return errors;
    }
}

// 用户实体
class User {
    @NotNull(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度必须在3-20之间")
    private String username;
    
    @NotNull
    @Email
    private String email;
    
    @Pattern(regexp = "^1[3-9]\d{9}$", message = "手机号格式不正确")
    private String phone;
    
    // 构造方法、getter、setter
    public User(String username, String email, String phone) {
        this.username = username;
        this.email = email;
        this.phone = phone;
    }
}

// 使用验证器
public class ValidationDemo {
    public static void main(String[] args) {
        // 测试数据验证
        User user1 = new User("", "invalid-email", "123");
        List<String> errors = Validator.validate(user1);
        System.out.println("验证错误:");
        errors.forEach(System.out::println);
        
        // 正确数据
        User user2 = new User("张三", "zhangsan@example.com", "13800138000");
        errors = Validator.validate(user2);
        System.out.println("验证通过: " + errors.isEmpty());
    }
}

案例2:API权限控制注解

java

// 权限注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
    String[] value();  // 需要的权限列表
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequireRole {
    String value();
}

// 权限上下文
class SecurityContext {
    private static ThreadLocal<User> currentUser = new ThreadLocal<>();
    
    public static void setCurrentUser(User user) {
        currentUser.set(user);
    }
    
    public static User getCurrentUser() {
        return currentUser.get();
    }
    
    public static void clear() {
        currentUser.remove();
    }
}

// 权限拦截器
class PermissionInterceptor {
    public static Object intercept(Object target, Method method, Object[] args) 
            throws Throwable {
        
        User currentUser = SecurityContext.getCurrentUser();
        if (currentUser == null) {
            throw new SecurityException("用户未登录");
        }
        
        // 检查角色权限
        if (method.isAnnotationPresent(RequireRole.class)) {
            RequireRole roleAnnotation = method.getAnnotation(RequireRole.class);
            String requiredRole = roleAnnotation.value();
            
            if (!currentUser.getRoles().contains(requiredRole)) {
                throw new SecurityException("需要角色: " + requiredRole);
            }
        }
        
        // 检查权限
        if (method.isAnnotationPresent(RequirePermission.class)) {
            RequirePermission permAnnotation = method.getAnnotation(RequirePermission.class);
            String[] requiredPerms = permAnnotation.value();
            
            for (String requiredPerm : requiredPerms) {
                if (!currentUser.getPermissions().contains(requiredPerm)) {
                    throw new SecurityException("需要权限: " + requiredPerm);
                }
            }
        }
        
        // 权限检查通过,执行方法
        return method.invoke(target, args);
    }
}

// 业务服务
class UserService {
    @RequireRole("ADMIN")
    public void deleteUser(String userId) {
        System.out.println("删除用户: " + userId);
    }
    
    @RequirePermission({"USER_READ", "USER_WRITE"})
    public void updateUser(User user) {
        System.out.println("更新用户: " + user);
    }
    
    public void getUserInfo(String userId) {
        System.out.println("获取用户信息: " + userId);
    }
}

// 用户实体
class User {
    private String id;
    private Set<String> roles = new HashSet<>();
    private Set<String> permissions = new HashSet<>();
    
    public User(String id, Set<String> roles, Set<String> permissions) {
        this.id = id;
        this.roles = roles;
        this.permissions = permissions;
    }
    
    // getter方法
    public Set<String> getRoles() { return roles; }
    public Set<String> getPermissions() { return permissions; }
}

// 测试
public class PermissionDemo {
    public static void main(String[] args) throws Exception {
        UserService service = new UserService();
        
        // 设置普通用户
        Set<String> userRoles = new HashSet<>(Arrays.asList("USER"));
        Set<String> userPerms = new HashSet<>(Arrays.asList("USER_READ"));
        User user = new User("user1", userRoles, userPerms);
        SecurityContext.setCurrentUser(user);
        
        try {
            // 普通用户可以查看
            Method getMethod = UserService.class.getMethod("getUserInfo", String.class);
            PermissionInterceptor.intercept(service, getMethod, new Object[]{"123"});
            System.out.println("查看用户信息成功");
            
            // 普通用户不能删除(需要ADMIN角色)
            Method deleteMethod = UserService.class.getMethod("deleteUser", String.class);
            PermissionInterceptor.intercept(service, deleteMethod, new Object[]{"123"});
        } catch (SecurityException e) {
            System.out.println("权限不足: " + e.getMessage());
        } finally {
            SecurityContext.clear();
        }
    }
}

6.4.2 注解的工作流程

注解处理完整流程

text

定义阶段 -> 使用阶段 -> 处理阶段 -> 影响阶段

详细流程

  1. 定义阶段

    java

    // 定义注解
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation {
        String value();
    }
    
  2. 使用阶段

    java

    // 使用注解
    class MyClass {
        @MyAnnotation("测试")
        public void myMethod() {
        }
    }
    
  3. 处理阶段

    • 编译时处理:APT处理器生成代码
    • 运行时处理:通过反射读取注解

    java

    // 运行时处理
    Method method = MyClass.class.getMethod("myMethod");
    if (method.isAnnotationPresent(MyAnnotation.class)) {
        MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
        System.out.println("注解值: " + annotation.value());
    }
    
  4. 影响阶段

    • 改变编译行为:生成额外代码
    • 改变运行行为:执行额外逻辑
    • 提供元数据:供框架使用

注解生命周期图

text

源码文件(.java)
    ↓ 编译(@Retention(SOURCE))
编译时处理
    ↓ 编译(@Retention(CLASS))
字节码文件(.class)
    ↓ 类加载
运行时处理(@Retention(RUNTIME))
    ↓ 执行
影响程序行为

总结

  • 注解是Java强大的元数据机制
  • 合理使用注解可以大大提高代码的可读性和可维护性
  • 编译时注解用于代码生成,运行时注解用于动态处理
  • 实际应用中,注解常用于配置、验证、AOP等场景
  • 理解注解的原理和工作流程有助于更好地使用框架和设计系统

第七章:内部类与枚举

7.1 内部类的分类

7.1.1 成员内部类

解释:定义在类中,方法外的内部类,可以访问外部类的所有成员(包括私有成员)。

示例

java

public class Outer {
    private String outerField = "外部类字段";
    
    class Inner {
        public void print() {
            System.out.println("访问外部类字段: " + outerField);
        }
    }
}

7.1.2 局部内部类

解释:定义在方法或代码块中的内部类,作用域仅限于定义它的方法或代码块。

示例

java

public class Outer {
    public void method() {
        class LocalInner {
            public void print() {
                System.out.println("局部内部类");
            }
        }
        LocalInner inner = new LocalInner();
        inner.print();
    }
}

7.1.3 匿名内部类

解释:没有名字的内部类,通常用于实现接口或继承类,只能使用一次。

示例

java

interface Greeting {
    void greet();
}

public class Test {
    public static void main(String[] args) {
        Greeting greeting = new Greeting() {
            @Override
            public void greet() {
                System.out.println("Hello, 匿名内部类!");
            }
        };
        greeting.greet();
    }
}

7.1.4 静态内部类

解释:使用static修饰的内部类,不能直接访问外部类的非静态成员。

示例

java

public class Outer {
    private static String staticField = "静态字段";
    
    static class StaticInner {
        public void print() {
            System.out.println("只能访问外部类的静态字段: " + staticField);
        }
    }
}

7.1.5 各种内部类的区别和选择

类型访问权限内存占用使用场景
成员内部类可访问外部类所有成员持有外部类引用需要紧密耦合的场景
局部内部类只能访问final/有效final变量方法内有效方法内部需要特殊实现的场景
匿名内部类同局部内部类一次性使用事件监听、简单回调
静态内部类只能访问外部类静态成员不持有外部类引用不需要访问外部实例的场景

选择原则

  • 需要访问外部实例 → 成员内部类
  • 不需要访问外部实例 → 静态内部类
  • 只使用一次 → 匿名内部类
  • 方法内专用 → 局部内部类

7.2 内部类的原理和特性

7.2.1 内部类与外部类的访问关系

解释:编译器会为内部类自动添加一个指向外部类实例的引用。

示例

java

// 编译后相当于
public class Outer$Inner {
    private final Outer this$0;  // 编译器添加的引用
    
    Outer$Inner(Outer outer) {
        this.this$0 = outer;
    }
}

7.2.2 内部类的字节码分析

总结

  • 每个内部类都会生成独立的.class文件
  • 命名格式:外部类名$内部类名.class
  • 匿名内部类:外部类名$数字.class

7.2.3 内部类的内存泄漏问题

问题:内部类持有外部类引用,如果内部类对象生命周期长于外部类,会导致外部类无法被回收。

解决方案

java

// 1. 使用静态内部类
static class StaticInner {}

// 2. 使用弱引用
class Inner {
    private WeakReference<Outer> outerRef;
}

7.2.4 内部类的序列化

注意事项

  • 内部类默认没有无参构造器
  • 隐式持有外部类引用
  • 建议使用静态内部类进行序列化

7.2.5 内部类的最佳实践

  1. 优先使用静态内部类
  2. 避免在频繁调用的方法中使用匿名内部类
  3. 注意内存泄漏问题
  4. 合理使用访问权限

7.3 枚举类型

7.3.1 枚举的定义和使用

解释:枚举是一种特殊的类,表示一组固定的常量。

示例

java

public enum Day {
    MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

// 使用
Day today = Day.MONDAY;

7.3.2 枚举的底层实现原理

原理

  • 枚举本质是继承java.lang.Enum的final类
  • 每个枚举常量都是类的实例
  • 私有构造器,防止外部实例化

编译后相当于

java

public final class Day extends Enum<Day> {
    public static final Day MONDAY = new Day("MONDAY", 0);
    // ... 其他常量
}

7.3.3 枚举的常用方法

java

Day day = Day.MONDAY;

// 获取名字
String name = day.name();  // "MONDAY"

// 获取序号
int ordinal = day.ordinal();  // 0

// 转换为字符串
String str = day.toString();

// 根据名字获取枚举
Day value = Day.valueOf("MONDAY");

// 获取所有枚举值
Day[] values = Day.values();

7.3.4 枚举与常量类的对比

特性枚举常量类
类型安全
可遍历
可添加方法
序列化安全
编译时检查

示例对比

java

// 常量类(不推荐)
class Color {
    public static final int RED = 1;
    public static final int GREEN = 2;
}

// 枚举(推荐)
enum Color {
    RED, GREEN, BLUE
}

7.3.5 枚举的高级用法

枚举实现接口

java

interface Operation {
    double apply(double x, double y);
}

enum BasicOperation implements Operation {
    PLUS {
        public double apply(double x, double y) { return x + y; }
    },
    MINUS {
        public double apply(double x, double y) { return x - y; }
    };
}

枚举集合

java

EnumSet<Day> weekdays = EnumSet.range(Day.MONDAY, Day.FRIDAY);
EnumMap<Day, String> schedule = new EnumMap<>(Day.class);

7.3.6 枚举的设计模式应用

单例模式

java

public enum Singleton {
    INSTANCE;
    
    public void doSomething() {
        System.out.println("单例方法");
    }
}

// 使用
Singleton.INSTANCE.doSomething();

优点:线程安全、防止反射攻击、自动序列化

7.4 枚举与注解的结合

7.4.1 枚举作为注解参数

java

@interface RequestMapping {
    HttpMethod[] method() default {};
}

enum HttpMethod {
    GET, POST, PUT, DELETE
}

// 使用
@RequestMapping(method = {HttpMethod.GET, HttpMethod.POST})
public class MyController {}

7.4.2 枚举在策略模式中的应用

java

enum Calculator {
    ADD {
        public int calculate(int a, int b) { return a + b; }
    },
    SUBTRACT {
        public int calculate(int a, int b) { return a - b; }
    };
    
    public abstract int calculate(int a, int b);
}

7.4.3 枚举的状态机实现

java

enum State {
    NEW {
        public State next() { return RUNNING; }
    },
    RUNNING {
        public State next() { return STOPPED; }
    },
    STOPPED {
        public State next() { return this; }
    };
    
    public abstract State next();
}