Java 泛型和反射(15/30)
目录
Java 泛型和反射
1. Java 泛型
1.1 泛型类和泛型方法
泛型类
泛型方法
1.2 泛型的通配符
2. Java 反射
2.1 反射的基础
2.2 动态创建对象和调用方法
3. 泛型和反射的结合
3.1 类型擦除
总结与后续
Java 泛型和反射
泛型 和 反射 是 Java 中的两个非常强大的特性,分别用于编写类型安全的代码和动态操作类与对象。泛型使得代码在编译时就能检测到类型错误,而反射则允许程序在运行时获取关于类的详细信息并操作它们。通过学习泛型和反射,开发者可以编写更加通用、灵活和强大的 Java 应用程序。本模块将详细介绍 Java 泛型的概念、使用方法以及反射的基本原理与应用。
1. Java 泛型
Java 的 泛型(Generics)是一个用于编写类型安全和通用代码的工具。通过泛型,开发者可以在类、接口或方法中定义类型参数,以确保在使用时能够处理不同的数据类型,同时避免不必要的类型转换。
1.1 泛型类和泛型方法
泛型类
泛型类是具有类型参数的类,类型参数可以在创建对象时指定。
示例:泛型类
public class Box<T> {private T item;public void setItem(T item) {this.item = item;}public T getItem() {return item;}public static void main(String[] args) {Box<String> stringBox = new Box<>();stringBox.setItem("Hello World");System.out.println(stringBox.getItem());Box<Integer> intBox = new Box<>();intBox.setItem(123);System.out.println(intBox.getItem());}
}
在这个例子中,Box<T>
是一个泛型类,T
是类型参数。在使用时可以指定不同的类型,例如 String
或 Integer
,从而使代码更加灵活。
泛型方法
泛型方法是带有类型参数的方法,允许方法在调用时指定不同的类型。
示例:泛型方法
public class GenericMethodExample {public static <T> void printArray(T[] array) {for (T element : array) {System.out.println(element);}}public static void main(String[] args) {Integer[] intArray = {1, 2, 3};String[] strArray = {"A", "B", "C"};printArray(intArray);printArray(strArray);}
}
在这个例子中,printArray
是一个泛型方法,它可以接受任何类型的数组,并将数组元素打印出来。
1.2 泛型的通配符
Java 提供了 通配符(Wildcard)来增强泛型的灵活性。
-
?
通配符:表示未知的类型。 -
上界通配符
<? extends T>
:表示类型必须是T
或其子类。 -
下界通配符
<? super T>
:表示类型必须是T
或其超类。
示例:使用通配符
import java.util.List;public class WildcardExample {public static void printList(List<? extends Number> list) {for (Number num : list) {System.out.println(num);}}public static void main(String[] args) {List<Integer> intList = List.of(1, 2, 3);List<Double> doubleList = List.of(1.1, 2.2, 3.3);printList(intList);printList(doubleList);}
}
在这个例子中,printList
方法使用上界通配符来接受任何类型为 Number
或其子类的列表。
2. Java 反射
反射(Reflection)是 Java 提供的一种机制,允许程序在运行时检查和操作类、方法、字段等信息。通过反射,开发者可以动态地创建对象、调用方法和修改字段值。
2.1 反射的基础
Java 反射的核心类是 Class
,它表示运行时的类或接口。通过反射,开发者可以获取类的结构和方法信息。
示例:获取类的信息
public class ReflectionExample {public static void main(String[] args) throws ClassNotFoundException {Class<?> clazz = Class.forName("java.util.ArrayList");System.out.println("类名: " + clazz.getName());System.out.println("方法列表:");for (var method : clazz.getMethods()) {System.out.println(method.getName());}}
}
在这个例子中,我们使用 Class.forName()
方法获取了 ArrayList
类的 Class
对象,并打印了类名和方法列表。
2.2 动态创建对象和调用方法
反射不仅可以获取类的信息,还可以动态创建对象并调用方法。
示例:动态创建对象并调用方法
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;public class ReflectionInvocationExample {public static void main(String[] args) {try {// 获取类的引用Class<?> clazz = Class.forName("java.lang.StringBuilder");// 动态创建对象Constructor<?> constructor = clazz.getConstructor();Object stringBuilderInstance = constructor.newInstance();// 动态调用 append 方法Method appendMethod = clazz.getMethod("append", String.class);appendMethod.invoke(stringBuilderInstance, "Hello Reflection");// 调用 toString 方法Method toStringMethod = clazz.getMethod("toString");String result = (String) toStringMethod.invoke(stringBuilderInstance);System.out.println("结果: " + result);} catch (Exception e) {e.printStackTrace();}}
}
在这个例子中,我们使用反射动态创建了一个 StringBuilder
对象,并通过反射调用了其 append
和 toString
方法。
3. 泛型和反射的结合
在使用泛型时,由于 类型擦除 的存在,泛型类型在运行时被擦除为原始类型,这使得泛型和反射的结合变得复杂。
3.1 类型擦除
类型擦除 是指 Java 编译器在编译时会将泛型信息擦除,替换为它们的边界类型。例如,List<T>
会被擦除为 List
,类型参数 T
会被替换为 Object
或其边界类型。
示例:类型擦除的影响
import java.util.ArrayList;
import java.util.List;public class TypeErasureExample {public static void main(String[] args) {List<String> stringList = new ArrayList<>();List<Integer> intList = new ArrayList<>();System.out.println(stringList.getClass() == intList.getClass()); // 输出: true}
}
在这个例子中,stringList
和 intList
的 Class
对象相等,因为泛型类型在运行时被擦除为相同的原始类型 List
。
总结与后续
在本模块中,我们学习了 Java 中的泛型和反射,包括如何使用泛型编写类型安全和通用的代码,以及如何使用反射在运行时动态地操作类和对象。泛型和反射的结合虽然有一些复杂性,但通过理解类型擦除,我们可以更好地在开发中利用这两种技术。
在下一模块中,我们将探讨 Java 序列化和反序列化,学习如何将对象转换为字节流进行存储或传输,以及如何恢复对象的状态。