1. 反射概述
1.1 什么是反射
反射(Reflection)是 Java 提供的强大特性,允许程序在运行时动态地获取、访问类的所有信息(包括类名、属性、方法、构造器、注解等),并能动态操作这些信息,突破编译期的访问限制。
1.2 反射的作用
- 获取类信息:运行时获取对象所属类的名称、包名、父类、实现的接口等;
- 动态创建对象 :无需
new关键字,通过类名字符串构造任意类的对象; - 访问成员变量:获取类的所有变量(包括私有、final 变量),并读写其值;
- 调用方法:获取类的所有方法(包括私有方法),并动态调用;
- 解析注解:运行时读取类 / 方法 / 属性上的注解,是框架实现注解驱动的核心。
2. Class 类:反射的根源
2.1 Class 类概述
JVM 加载完类后,会在堆内存的方法区中生成一个Class类型的对象 ------ 这个对象是该类的 "说明书",包含了类的所有信息(类名、父类、接口、属性、方法、构造器等)。
核心特点:
- 每个类在 JVM 中只有一份字节码 ,对应唯一的
Class对象; Class对象是反射的入口,所有反射操作都基于Class对象展开;- 无论是自定义的
Person类,还是 JDK 内置的String/ArrayList,加载后都会生成对应的Class对象。
2.2 获取 Class 对象的三种方式(附案例)
获取Class对象有三种核心方式,以下以Student类为例演示:
第一步:定义 Student 测试类
java
运行
public class Student {
private String name;
private int age;
private String address;
// 构造方法:私有、默认、公共(多参)
public Student() {}
private Student(String name) {this.name = name;}
Student(String name, int age) {this.name = name; this.age = age;}
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
// 成员方法:私有、公共(无参/有参/有返回值)
private void function() {System.out.println("执行function()");}
public void method1() {System.out.println("执行method1()");}
public void method2(String s) {System.out.println("执行method2():" + s);}
public String method3(String s, int i) {return "执行method3():"+s + "," + i;}
@Override
public String toString() {
return "Student{" + "name='" + name + '\'' + ", age=" + age + ", address='" + address + '\'' + '}';
}
}
第二步:获取 Class 对象的三种方式
java
运行
public class ClassTest {
public static void main(String[] args) throws ClassNotFoundException {
// 方式1:类.class(编译期确定,最安全)
System.out.println("---------方式1:类.class---------");
Class<Student> c1 = Student.class;
System.out.println(c1); // class com.hg.java.Student
Class<Student> c2 = Student.class;
System.out.println(c1 == c2); // true(同一Class对象)
// 方式2:对象.getClass()(运行时获取,适用于已有对象的场景)
System.out.println("---方式2:对象.getClass()---");
Student s = new Student();
Class<? extends Student> c3 = s.getClass();
System.out.println(c1 == c3); // true
// 方式3:Class.forName(全类名)(动态加载,最灵活,适用于配置文件场景)
System.out.println("----方式3:Class.forName(全类名)----");
Class<?> c4 = Class.forName("com.hg.java.Student");
System.out.println(c1 == c4); // true
}
}
3. Constructor:操作类的构造器
3.1 Constructor 类概述
java.lang.reflect.Constructor是反射体系中描述类的构造器的类:
- 类的每个构造器(无参 / 有参、公有 / 私有)都对应一个
Constructor对象; - 通过
Constructor对象可在运行时创建类的实例,甚至调用私有构造器。
3.2 获取 Constructor 对象的方法(附案例)
| 方法 | 说明 |
|---|---|
Constructor<?>[] getConstructors() |
返回所有公共构造方法的数组 |
Constructor<?>[] getDeclaredConstructors() |
返回所有构造方法的数组(含私有) |
Constructor getConstructor(Class<?>... parameterTypes) |
返回单个公共构造方法(指定参数类型) |
Constructor getDeclaredConstructor(Class<?>...parameterTypes) |
返回单个构造方法(含私有,指定参数类型) |
案例:获取 Student 类的构造器
java
运行
public class ConstructorTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class<?> c = Class.forName("com.hg.java1.Student");
// 1. 获取所有构造器(含私有)
System.out.println("---返回所有构造方法的数组----");
Constructor<?>[] cons = c.getDeclaredConstructors();
for (Constructor con : cons) {
System.out.println(con);
}
// 2. 获取单个公共构造器
System.out.println("-----返回单个构造方法----");
Constructor<?> con = c.getConstructor(); // 无参构造
System.out.println(con);
con = c.getConstructor(String.class, int.class, String.class); // 三参构造
System.out.println(con);
}
}
3.3 Constructor 常用方法(附案例)
核心方法:T newInstance(Object...initargs) ------ 根据指定构造器创建对象。
java
运行
public class ConstructorTest {
public static void main(String[] args) throws Exception {
Class<?> c = Class.forName("com.hg.java1.Student");
// 1. 通过无参构造创建对象
System.out.println("------通过反射获取公共构造方法并创建对象--------");
Constructor<?> con = c.getConstructor();
Student s1 = (Student) con.newInstance();
System.out.println(s1); // Student{name='null', age=0, address='null'}
// 2. 通过三参构造创建对象
con = c.getConstructor(String.class, int.class, String.class);
Object obj = con.newInstance("林青霞", 30, "西安");
System.out.println(obj); // Student{name='林青霞', age=30, address='西安'}
}
}
4. Field:操作类的成员变量
4.1 Field 类概述
java.lang.reflect.Field是反射体系中描述类的成员变量的类:
- 每个成员变量(公有 / 私有、静态 / 实例、final)都对应一个
Field对象; - 通过
Field对象可在运行时读写变量值,突破访问权限限制。
4.2 获取 Field 对象的方法(附案例)
| 方法 | 说明 |
|---|---|
Field[] getFields() |
返回所有公共成员变量的数组 |
Field[] getDeclaredFields() |
返回所有成员变量的数组(含私有) |
Field getField(String name) |
返回单个公共成员变量(指定变量名) |
Field getDeclaredField(String name) |
返回单个成员变量(含私有,指定变量名) |
案例:获取 Student 类的成员变量
java
运行
public class FieldTest {
public static void main(String[] args) throws Exception {
Class<?> c = Class.forName("com.hg.java1.Student");
// 1. 获取所有公共成员变量
System.out.println("---------返回所有公共成员变量的数组----------");
Field[] fields = c.getFields();
for (Field field : fields) {
System.out.println(field);
}
// 2. 获取所有成员变量(含私有)
System.out.println("---------返回所有成员变量的数组----------");
fields = c.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
// 3. 获取单个公共成员变量
System.out.println("---------返回单个公共成员变量----------");
Field addressField = c.getField("address");
System.out.println(addressField);
// 4. 获取单个私有成员变量
System.out.println("---------返回单个私有成员变量----------");
Field nameField = c.getDeclaredField("name");
System.out.println(nameField);
}
}
4.3 Field 常用方法(附案例)
核心方法:void set(Object obj,Object value) ------ 给指定对象的成员变量赋值。
java
运行
public class FieldTest {
public static void main(String[] args) throws Exception{
Class<?> c = Class.forName("com.hg.java1.Student");
// 先通过构造器创建对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
// 1. 给公共变量address赋值
Field addressField = c.getField("address");
addressField.set(obj, "西安");
System.out.println(obj); // Student{name='null', age=0, address='西安'}
// 2. 给私有变量age赋值(默认禁止访问,需先解除限制)
Field ageField = c.getDeclaredField("age");
ageField.setAccessible(true); // 解除访问权限检查
ageField.set(obj, 30);
System.out.println(obj); // Student{name='null', age=30, address='西安'}
}
}
5. Method:操作类的成员方法
5.1 Method 类概述
java.lang.reflect.Method是反射体系中描述类的成员方法的类:
- 每个方法(公有 / 私有、静态 / 实例、有参 / 无参)都对应一个
Method对象; - 通过
Method对象可在运行时调用方法,包括私有方法。
5.2 获取 Method 对象的方法(附案例)
| 方法 | 说明 |
|---|---|
Method[] getMethods() |
返回所有公共成员方法的数组(含父类继承的) |
Method[] getDeclaredMethods() |
返回所有成员方法的数组(仅本类,含私有) |
Method getMethod(String name, Class<?>... parameterTypes) |
返回单个公共方法(指定方法名 + 参数类型) |
Method getDeclaredMethod(String name, Class<?>... parameterTypes) |
返回单个方法(含私有,指定方法名 + 参数类型) |
案例:获取 Student 类的成员方法
java
运行
public class MethodTest {
public static void main(String[] args) throws Exception {
Class<?> c = Class.forName("com.hg.java1.Student");
// 1. 获取所有公共方法(含toString、equals等父类方法)
System.out.println("-----------返回所有公共成员方法的数组--------");
Method[] methods = c.getMethods();
for (Method method : methods) {
System.out.println(method);
}
// 2. 获取所有本类方法(含私有)
System.out.println("-----------返回所有成员方法的数组--------");
methods = c.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}
// 3. 获取单个公共方法
System.out.println("-----------返回单个公共成员方法--------");
Method method = c.getMethod("method1"); // 无参
System.out.println(method);
Method method2 = c.getMethod("method2", String.class); // 单参
System.out.println(method2);
Method method3 = c.getMethod("method3", String.class, int.class); // 双参
System.out.println(method3);
// 4. 获取私有方法
System.out.println("-----------返回单个非公共成员方法--------");
Method method4 = c.getDeclaredMethod("function");
System.out.println(method4);
}
}
5.3 Method 常用方法(附案例)
核心方法:Object invoke(Object obj,Object... args) ------ 调用指定对象的方法,参数为args,返回值为方法执行结果。
java
运行
public class MethodTest {
public static void main(String[] args) throws Exception {
Class<?> c = Class.forName("com.hg.java1.Student");
// 创建对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
// 1. 调用无参公共方法
Method method = c.getMethod("method1");
method.invoke(obj); // 执行method1()
// 2. 调用单参公共方法
Method method2 = c.getMethod("method2", String.class);
method2.invoke(obj, "林青霞"); // 执行method2():林青霞
// 3. 调用有返回值的公共方法
Method method3 = c.getMethod("method3", String.class, int.class);
Object o = method3.invoke(obj, "林青霞", 30);
System.out.println(o); // 执行method3():林青霞,30
// 4. 调用私有方法(需解除访问限制)
Method method4 = c.getDeclaredMethod("function");
method4.setAccessible(true);
method4.invoke(obj); // 执行function()
}
}
6. Annotation:操作类的注解
6.1 Annotation 类概述
java.lang.annotation.Annotation是反射体系中描述注解的类:
- 每个加在类 / 方法 / 属性上的注解,都对应一个
Annotation对象; - 反射可运行时读取注解的属性值,判断元素是否被注解标记(框架核心用法);
- 注解必须标注
@Retention(RetentionPolicy.RUNTIME),否则反射无法读取。
6.2 获取 Annotation 对象的方法(附案例)
| 方法 | 说明 |
|---|---|
Annotation[] getAnnotations() |
返回元素上的所有注解(含继承的) |
Annotation[] getDeclaredAnnotations() |
返回元素上的所有注解(仅本元素) |
Annotation getAnnotation(Class<?> annoClass) |
返回指定类型的注解(含继承的) |
Annotation getDeclaredAnnotation(Class<?> annoClass) |
返回指定类型的注解(仅本元素) |
isAnnotationPresent(Class<A> annoClass) |
判断元素是否被指定注解标记 |
案例:解析类 / 属性 / 方法上的注解
java
运行
import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.annotation.Annotation;
public class AnnotationTest {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("com.hg.java1.SampleClass");
// 1. 解析类上的注解
System.out.println("----------解析类上的注解---------");
if (clazz.isAnnotationPresent(CustomAnnotation.class)) {
CustomAnnotation customAnnotation = clazz.getAnnotation(CustomAnnotation.class);
System.out.println("类注解-name: " + customAnnotation.name());
System.out.println("类注解-value: " + customAnnotation.value());
}
// 2. 解析属性上的注解
System.out.println("----------解析属性上的注解---------");
Field field = clazz.getDeclaredField("sampleField");
if (field.isAnnotationPresent(CustomAnnotation.class)) {
CustomAnnotation customAnnotation = field.getAnnotation(CustomAnnotation.class);
System.out.println("属性注解-name: " + customAnnotation.name());
System.out.println("属性注解-value: " + customAnnotation.value());
}
// 3. 解析方法上的注解
System.out.println("----------解析方法上的注解---------");
Method method = clazz.getMethod("getSampleField");
if (method.isAnnotationPresent(CustomAnnotation.class)) {
CustomAnnotation customAnnotation = method.getAnnotation(CustomAnnotation.class);
System.out.println("方法注解-name: " + customAnnotation.name());
System.out.println("方法注解-value: " + customAnnotation.value());
}
}
}
// 自定义注解(必须指定RUNTIME保留策略)
@Retention(RetentionPolicy.RUNTIME)
@interface CustomAnnotation {
String name() default "这是name";
String value() default "这是value";
}
// 测试类
@CustomAnnotation(name="SampleClass", value = "Sample Class Annotation")
class SampleClass {
@CustomAnnotation(name="sampleField", value = "Sample Field Annotation")
private String sampleField;
@CustomAnnotation(name="getSampleMethod", value = "Sample Method Annotation")
public String getSampleField() {
return sampleField;
}
public void setSampleField(String sampleField) {
this.sampleField = sampleField;
}
}
7. 实战作业(3 道经典反射练习题)
作业 1:通过反射越过泛型检查,给 ArrayList<Integer>添加字符串
要求:反射突破泛型的编译期检查,往ArrayList<Integer>中添加字符串数据。
java
运行
// 提示:泛型仅在编译期生效,运行时会被擦除,反射可直接调用add方法
ArrayList<Integer> intList = new ArrayList<>();
intList.add(100);
// 请补充反射代码,添加字符串"Hello Reflection"
作业 2:通过反射运行配置文件中指定类的指定方法
要求:使用Properties解析配置文件,反射调用指定类的指定方法。
-
配置文件
student.properties:properties
className=com.java.exer.Student methodName=study -
Student 类: java
运行
public class Student { public void study() { System.out.println("好好学习天天向上"); } }
作业 3:反射解析注解并动态调用方法
要求:
- 判断
StudentController类是否添加@MyController注解; - 若添加,则将标注
@MyRequestMapping的方法存入HashMap; - 控制台输入方法名,调用对应的方法。
-
自定义注解: java
运行
@Retention(RetentionPolicy.RUNTIME) public @interface MyController { String name() default "这是name"; String value() default "这是value"; } @Retention(RetentionPolicy.RUNTIME) public @interface MyRequestMapping { String name() default "这是name"; String value() default "这是value"; } -
StudentController 类: java
运行
@MyController public class StudentController { @MyRequestMapping public void method1() { System.out.println("执行了method1()"); } public void method2() { System.out.println("执行了method2()"); } @MyRequestMapping public void method3() { System.out.println("执行了method3()"); } }
总结
反射是 Java 中极具威力的特性,核心是通过 JVM 中的类元数据突破编译期限制,实现动态化编程。掌握Class、Constructor、Field、Method、Annotation的使用,是理解 Spring 等框架底层原理的关键。建议结合本文案例动手实践,再完成课后作业,彻底掌握反射的核心用法。