一、什么是反射?
1.1 定义
反射(Reflection) 是Java语言提供的一种强大机制,允许程序在运行时动态地获取类的完整信息(如成员变量、方法、构造方法等),并且可以动态地创建对象、调用方法、访问属性。
通俗地说:反射就是在程序运行时,让Java程序能够“ introspection”(自省),看清自己的结构,甚至修改自己的行为。
1.2 正常方式 vs 反射方式
对比项 正常方式 反射方式
时机 编译时确定 运行时动态确定
类的信息 写代码时已知 运行时才获取
对象创建 new 类名() Class.forName(“类名”).newInstance()
方法调用 对象.方法名() method.invoke(对象, 参数)
特点 高效、类型安全 灵活、但有一定性能开销
简单示例:
// 正常方式:编译时就知道要使用Student类
Student s = new Student();
s.study();
// 反射方式:运行时才知道要使用哪个类
Class<?> clazz = Class.forName(“com.example.Student”);
Object obj = clazz.newInstance();
Method method = clazz.getMethod(“study”);
method.invoke(obj);
AI写代码
java
运行
二、为什么需要反射?
2.1 核心价值
反射机制使得Java程序从“静态”变为“动态”:
能力 说明
动态加载类 运行时才决定加载哪个类
动态创建对象 类名作为字符串传入,无需硬编码
动态调用方法 方法名作为字符串传入
访问私有成员 可以绕过访问权限检查
2.2 典型应用场景
场景 说明
IDE自动提示 编辑器通过反射获取类的方法和属性
Spring框架 IoC容器通过反射创建和管理Bean
JUnit框架 通过反射识别@Test注解的方法并执行
Tomcat等Web容器 动态加载Servlet类
JSON解析库 通过反射将JSON数据映射到Java对象
注解处理器 运行时读取注解信息
三、反射的核心API
3.1 Class类
Class类是反射的入口,每个Java类在运行时都会对应一个Class对象。
获取Class对象的三种方式:
// 方式1:通过类名.class
Class<?> clazz1 = String.class;
// 方式2:通过对象.getClass()
String str = “hello”;
Class<?> clazz2 = str.getClass();
// 方式3:通过Class.forName()(最常用,动态加载)
Class<?> clazz3 = Class.forName(“java.lang.String”);
AI写代码
java
运行
3.2 反射核心类
类名 作用
Class 代表一个类或接口
Field 代表类的成员变量
Method 代表类的方法
Constructor 代表类的构造方法
Modifier 访问修饰符的工具类
3.3 常用方法一览
Class类常用方法:
方法 说明
getName() 获取类全名(包名+类名)
getSimpleName() 获取类简单名称
getFields() 获取所有public成员变量
getDeclaredFields() 获取所有声明的成员变量(包括private)
getMethods() 获取所有public方法(包括继承的)
getDeclaredMethods() 获取所有声明的方法(不包括继承的)
getConstructors() 获取所有public构造方法
getDeclaredConstructors() 获取所有声明的构造方法
newInstance() 创建实例(调用无参构造)
四、反射的基本使用
4.1 准备工作:定义一个示例类
/**
示例类:用于演示反射机制
*/
public class Person {
// 成员变量
private String name;
public int age;// 构造方法
public Person() {
System.out.println(“无参构造方法被调用”);
}private Person(String name) {
this.name = name;
System.out.println(“私有构造方法被调用,name=” + name);
}public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println(“有参构造方法被调用:” + name + “,” + age);
}// 成员方法
public void sayHello() {
System.out.println(“Hello, I’m “ + name);
}private void secret() {
System.out.println(“这是一个私有方法”);
}public String getName() {
return name;
}public void setName(String name) {
this.name = name;
}@Override
public String toString() {
return “Person{name=’” + name + “‘, age=” + age + “}”;
}
}
AI写代码
java
运行
4.2 获取类的信息
import java.lang.reflect.*;
public class ReflectionInfoDemo {
public static void main(String[] args) throws ClassNotFoundException {
// 1. 获取Class对象
Class<?> clazz = Class.forName(“Person”);
// 2. 获取类名
System.out.println("类全名:" + clazz.getName());
System.out.println("简单类名:" + clazz.getSimpleName());
// 3. 获取所有public方法(包括继承的)
System.out.println("\n=== 所有public方法 ===");
Method[] methods = clazz.getMethods();
for (Method m : methods) {
System.out.println(" " + m.getName());
}
// 4. 获取所有声明的成员变量
System.out.println("\n=== 所有声明的成员变量 ===");
Field[] fields = clazz.getDeclaredFields();
for (Field f : fields) {
System.out.println(" " + Modifier.toString(f.getModifiers()) + " " + f.getName());
}
// 5. 获取所有构造方法
System.out.println("\n=== 所有构造方法 ===");
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
for (Constructor<?> c : constructors) {
System.out.println(" " + c);
}
}
}
AI写代码
java
运行
4.3 通过反射创建对象
import java.lang.reflect.*;
public class ReflectionCreateDemo {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName(“Person”);
// 方式1:调用无参构造方法
System.out.println("=== 方式1:无参构造 ===");
Person p1 = (Person) clazz.newInstance(); // JDK9后可能过时
p1.setName("张三");
p1.sayHello();
// 方式2:调用有参构造方法(推荐)
System.out.println("\n=== 方式2:有参构造 ===");
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Person p2 = (Person) constructor.newInstance("李四", 20);
p2.sayHello();
// 方式3:调用私有构造方法
System.out.println("\n=== 方式3:私有构造 ===");
Constructor<?> privateCon = clazz.getDeclaredConstructor(String.class);
privateCon.setAccessible(true); // 突破私有访问限制
Person p3 = (Person) privateCon.newInstance("王五");
p3.sayHello();
}
}
AI写代码
java
运行
4.4 通过反射调用方法
import java.lang.reflect.*;
public class ReflectionMethodDemo {
public static void main(String[] args) throws Exception {
// 创建对象
Class> clazz = Class.forName("Person");
Constructor> con = clazz.getConstructor(String.class, int.class);
Person person = (Person) con.newInstance(“小明”, 18);
// 调用public方法
System.out.println("=== 调用public方法 ===");
Method sayHello = clazz.getMethod("sayHello");
sayHello.invoke(person);
// 调用带参数的方法
Method setName = clazz.getMethod("setName", String.class);
setName.invoke(person, "小红");
sayHello.invoke(person);
// 调用私有方法(需要setAccessible)
System.out.println("\n=== 调用私有方法 ===");
Method secret = clazz.getDeclaredMethod("secret");
secret.setAccessible(true); // 突破私有权限
secret.invoke(person);
// 调用有返回值的方法
Method getName = clazz.getMethod("getName");
String name = (String) getName.invoke(person);
System.out.println("getName返回值:" + name);
}
}
AI写代码
java
运行
4.5 通过反射访问成员变量
以下是整理后的代码块格式:
import java.lang.reflect.*;
public class ReflectionFieldDemo {
public static void main(String[] args) throws Exception {
// 创建对象
Class> clazz = Class.forName("Person");
Constructor> con = clazz.getConstructor(String.class, int.class);
Person person = (Person) con.newInstance(“张三”, 25);
System.out.println("原始对象:" + person);
// 访问public成员变量
System.out.println("\n=== 访问public成员变量 ===");
Field ageField = clazz.getField("age");
System.out.println("当前age:" + ageField.get(person));
ageField.set(person, 30);
System.out.println("修改后age:" + ageField.get(person));
// 访问私有成员变量(需要setAccessible)
System.out.println("\n=== 访问私有成员变量 ===");
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
System.out.println("当前name:" + nameField.get(person));
nameField.set(person, "李四");
System.out.println("修改后name:" + nameField.get(person));
System.out.println("最终对象:" + person);
}
}
AI写代码
java
运行
五、反射的实际应用示例
示例1:通用的toString方法
import java.lang.reflect.*;
class ObjectUtil {
public static String toString(Object obj) {
if (obj == null) return “null”;
Class<?> clazz = obj.getClass();
StringBuilder sb = new StringBuilder();
sb.append(clazz.getSimpleName()).append(" { ");
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
fields[i].setAccessible(true);
try {
sb.append(fields[i].getName()).append("=");
sb.append(fields[i].get(obj));
if (i < fields.length - 1) {
sb.append(", ");
}
} catch (IllegalAccessException e) {
sb.append("?");
}
}
sb.append(" }");
return sb.toString();
}
}
class Student {
private String name;
private int score;
Student(String name, int score) {
this.name = name;
this.score = score;
}
}
public class GenericToStringDemo {
public static void main(String[] args) {
Student s = new Student(“张三”, 95);
System.out.println(ObjectUtil.toString(s));
}
}
AI写代码
java
运行
示例2:动态调用任意方法
import java.lang.reflect.*;
class DynamicInvoker {
public static Object invoke(Object obj, String methodName, Object… args) {
try {
Class> clazz = obj.getClass();
Class>[] paramTypes = new Class<?>[args.length];
for (int i = 0; i < args.length; i++) {
paramTypes[i] = args[i].getClass();
}
Method method = clazz.getMethod(methodName, paramTypes);
return method.invoke(obj, args);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
class Calculator {
public int add(int a, int b) {
return a + b;
}
public String greet(String name) {
return "Hello, " + name;
}
}
public class DynamicInvokeDemo {
public static void main(String[] args) {
Calculator calc = new Calculator();
Object result1 = DynamicInvoker.invoke(calc, “add”, 10, 20);
System.out.println(“add结果:” + result1);
Object result2 = DynamicInvoker.invoke(calc, “greet”, “张三”);
System.out.println(“greet结果:” + result2);
}
}
AI写代码
java
运行
示例3:模拟JUnit的@Test注解
import java.lang.annotation.;
import java.lang.reflect.;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Test {}
class MyTestClass {
@Test
public void testMethod1() {
System.out.println(“执行测试方法1”);
}
@Test
public void testMethod2() {
System.out.println("执行测试方法2");
}
public void normalMethod() {
System.out.println("普通方法不会被自动执行");
}
}
class TestRunner {
public static void run(Class<?> testClass) throws Exception {
Object instance = testClass.newInstance();
Method[] methods = testClass.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(Test.class)) {
System.out.print("正在执行:");
method.invoke(instance);
}
}
}
}
public class AnnotationDemo {
public static void main(String[] args) throws Exception {
TestRunner.run(MyTestClass.class);
}
}
AI写代码
java
运行
运行结果
正在执行:执行测试方法1
正在执行:执行测试方法2
AI写代码
六、反射的优缺点
6.1 优点
优点 说明
动态性 运行时动态加载类,提高程序灵活性
通用性 可以编写通用的代码处理各种类型的对象
框架基础 几乎所有Java框架(Spring、Hibernate等)都依赖反射
绕过限制 可以访问私有成员,用于特殊场景(如调试工具)
6.2 缺点
缺点 说明
性能开销 反射调用比直接调用慢,因为需要动态解析
安全问题 可以绕过访问权限,破坏封装性
代码可读性 反射代码不如直接调用直观
编译时检查缺失 错误只能在运行时暴露
七、性能对比示例
以下是整理后的代码块格式:
/**
- 对比直接调用与反射调用的性能
*/
public class PerformanceCompare {
public static void main(String[] args) throws Exception {
Person person = new Person(“张三”, 20);
// 1. 直接调用
long start1 = System.nanoTime();
for (int i = 0; i < 1000000; i++) {
person.sayHello();
}
long end1 = System.nanoTime();
System.out.println(“直接调用耗时:” + (end1 - start1) / 1000000 + “ ms”);
// 2. 反射调用
Method method = Person.class.getMethod(“sayHello”);
long start2 = System.nanoTime();
for (int i = 0; i < 1000000; i++) {
method.invoke(person);
}
long end2 = System.nanoTime();
System.out.println(“反射调用耗时:” + (end2 - start2) / 1000000 + “ ms”);
// 3. 反射调用(启用访问检查关闭)
method.setAccessible(true);
long start3 = System.nanoTime();
for (int i = 0; i < 1000000; i++) {
method.invoke(person);
}
long end3 = System.nanoTime();
System.out.println(“反射(setAccessible)耗时:” + (end3 - start3) / 1000000 + “ ms”);
}
}
AI写代码
java
运行
代码结构说明:
完整保留了原始注释和逻辑结构
使用标准的Java代码块格式
保持了原有的三个测试部分(直接调用、反射调用、反射调用优化)
时间统计单位统一转换为毫秒(ms)
结论:反射调用通常比直接调用慢2-3倍,setAccessible(true)可以略微提升性能。
八、总结
8.1 核心知识点回顾
知识点 要点
反射的定义 运行时动态获取类信息并操作对象
获取Class对象 .class、getClass()、Class.forName()
核心API Class、Field、Method、Constructor
访问私有成员 setAccessible(true)
主要应用 Spring框架、JUnit、JSON解析等
8.2 使用建议
优先使用直接调用:除非确实需要动态性,否则不要滥用反射
缓存反射对象:如果需要频繁调用,缓存Method、Field等对象
注意安全性:反射可以破坏封装,谨慎使用
考虑性能影响:对性能敏感的代码避免使用反射
8.3 作业完成情况
理解反射机制的定义和作用
掌握获取Class对象的三种方式
能够通过反射创建对象、调用方法、访问属性
了解反射的实际应用场景
了解反射的优缺点
九、课后练习
编写代码,通过反射获取ArrayList类的所有方法名称
使用反射调用String类的toUpperCase()方法
通过反射创建一个HashMap对象,并调用put和get方法
尝试修改一个String对象的内容(利用反射突破不可变性)