跳到主要内容Java 反射详解 | 极客日志Javajava
Java 反射详解
Java 反射允许程序在运行时动态获取类信息和操作成员。涵盖 Class 对象获取方式、Constructor 构造器操作、Field 成员变量读写、Method 方法调用及 Annotation 注解解析。通过具体代码示例演示了如何突破编译期限制,包括私有成员访问和泛型擦除处理。最后提供实战练习题巩固理解,是掌握 Spring 等框架底层原理的基础。
苹果系统21 浏览 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 测试类
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 {
.name = name;
.age = age;
.address = address;
}
{ System.out.println(); }
{ System.out.println(); }
{ System.out.println( + s); }
String { + s + + i; }
String {
+ + name + + + age + + address + + ;
}
}
(String name, int age, String address)
this
this
this
private
void
function
()
"执行 function()"
public
void
method1
()
"执行 method1()"
public
void
method2
(String s)
"执行 method2:"
public
method3
(String s, int i)
return
"执行 method3:"
","
@Override
public
toString
()
return
"Student{"
"name='"
'\''
", age="
", address='"
'\''
'}'
第二步:获取 Class 对象的三种方式
public class ClassTest {
public static void main(String[] args) throws ClassNotFoundException {
System.out.println("---------方式 1:类.class---------");
Class<Student> c1 = Student.class;
System.out.println(c1);
Class<Student> c2 = Student.class;
System.out.println(c1 == c2);
System.out.println("---方式 2:对象.getClass()---");
Student s = new Student();
Class<? extends Student> c3 = s.getClass();
System.out.println(c1 == c3);
System.out.println("----方式 3:Class.forName(全类名)----");
Class<?> c4 = Class.forName("com.hg.java.Student");
System.out.println(c1 == c4);
}
}
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 类的构造器
public class ConstructorTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class<?> c = Class.forName("com.hg.java1.Student");
System.out.println("---返回所有构造方法的数组----");
Constructor<?>[] cons = c.getDeclaredConstructors();
for (Constructor con : cons) {
System.out.println(con);
}
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) —— 根据指定构造器创建对象。
public class ConstructorTest {
public static void main(String[] args) throws Exception {
Class<?> c = Class.forName("com.hg.java1.Student");
System.out.println("------通过反射获取公共构造方法并创建对象--------");
Constructor<?> con = c.getConstructor();
Student s1 = (Student) con.newInstance();
System.out.println(s1);
con = c.getConstructor(String.class, int.class, String.class);
Object obj = con.newInstance("林青霞", 30, "西安");
System.out.println(obj);
}
}
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 类的成员变量
public class FieldTest {
public static void main(String[] args) throws Exception {
Class<?> c = Class.forName("com.hg.java1.Student");
System.out.println("---------返回所有公共成员变量的数组----------");
Field[] fields = c.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("---------返回所有成员变量的数组----------");
fields = c.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("---------返回单个公共成员变量----------");
Field addressField = c.getField("address");
System.out.println(addressField);
System.out.println("---------返回单个私有成员变量----------");
Field nameField = c.getDeclaredField("name");
System.out.println(nameField);
}
}
4.3 Field 常用方法(附案例)
核心方法:void set(Object obj,Object value) —— 给指定对象的成员变量赋值。
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();
Field addressField = c.getField("address");
addressField.set(obj, "西安");
System.out.println(obj);
Field ageField = c.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(obj, 30);
System.out.println(obj);
}
}
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 类的成员方法
public class MethodTest {
public static void main(String[] args) throws Exception {
Class<?> c = Class.forName("com.hg.java1.Student");
System.out.println("-----------返回所有公共成员方法的数组--------");
Method[] methods = c.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("-----------返回所有成员方法的数组--------");
methods = c.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}
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);
System.out.println("-----------返回单个非公共成员方法--------");
Method method4 = c.getDeclaredMethod("function");
System.out.println(method4);
}
}
5.3 Method 常用方法(附案例)
核心方法:Object invoke(Object obj,Object... args) —— 调用指定对象的方法,参数为 args,返回值为方法执行结果。
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();
Method method = c.getMethod("method1");
method.invoke(obj);
Method method2 = c.getMethod("method2", String.class);
method2.invoke(obj, "林青霞");
Method method3 = c.getMethod("method3", String.class, int.class);
Object o = method3.invoke(obj, "林青霞", 30);
System.out.println(o);
Method method4 = c.getDeclaredMethod("function");
method4.setAccessible(true);
method4.invoke(obj);
}
}
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) | 判断元素是否被指定注解标记 |
案例:解析类 / 属性 / 方法上的注解
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");
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());
}
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());
}
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());
}
}
}
@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添加字符串
要求:反射突破泛型的编译期检查,往 ArrayList<Integer> 中添加字符串数据。
ArrayList<Integer> intList = new ArrayList<>();
intList.add(100);
作业 2:通过反射运行配置文件中指定类的指定方法
要求:使用 Properties 解析配置文件,反射调用指定类的指定方法。
public class Student {
public void study() {
System.out.println("好好学习天天向上");
}
}
className=com.java.exer.Student
methodName=study
作业 3:反射解析注解并动态调用方法
- 判断
StudentController 类是否添加 @MyController 注解;
- 若添加,则将标注
@MyRequestMapping 的方法存入 HashMap;
- 控制台输入方法名,调用对应的方法。
@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()");
}
}
@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";
}
总结
反射是 Java 中极具威力的特性,核心是通过 JVM 中的类元数据突破编译期限制,实现动态化编程。掌握 Class、Constructor、Field、Method、Annotation 的使用,是理解 Spring 等框架底层原理的关键。建议结合本文案例动手实践,再完成课后作业,彻底掌握反射的核心用法。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online