大家好!反射是 Java 中核心技术之一——它能让程序在运行时“看透”类的内部结构,甚至操作私有方法和属性,这在框架开发、配置化编程中至关重要。
这篇文章会,先讲清类加载与反射的关联,再拆解反射的核心操作,最后结合实战案例,帮你彻底吃透反射的核心知识点,应对面试和开发场景都游刃有余。
一、认识反射
1.1 什么是反射?
反射(Reflection)是 Java 提供的一种能力:程序在运行时,能够获取类的完整结构(包括类名、属性、方法、构造器等),并动态操作这些结构。简单说,就是“运行时探知类的一切”。
正常编程:比如 new User() 创建对象,编译期就知道要调用 User 类;
反射:即使编译时不知道类名,只要有类的全名,就能动态创建对象、调用方法。
1.2 反射的作用
- 可以得到一个类的全部成员然后操作。
- 可以破坏封装性。
- 可以绕过泛型的约束
1.3 反射的缺点
反射虽强,但有明显缺点,需避免滥用:
- 性能略差:跳过编译检查,直接操作类结构,比正常调用慢 10-100 倍
- 破坏封装:能直接操作私有属性和方法,违背面向对象的封装原则;
二、反射第一步:获取Class对象
反射的核心是“操作 Class 对象”——每个类在 JVM 中只会生成一个 Class 对象,它包含了类的所有信息。 Class 对象就是是反射的“入口”——所有反射操作,都必须先获取目标类的 Class 对象。
2 获取 Class 对象的 3 种核心方式
- Class c1 = 类名.class
- 调用Class提供方法:public static Class forName(String package);
- Object提供的方法: public Class getClass(); Class c3 = 对象.getClass();
public static void main(String[] args) throws Exception {
//目标:反射第一步获取class对象
// Class c1 = 类名.class
Class clazz = Demo01.class;
System.out.println(clazz);
//调用Class提供方法: public static Class forName(String package);
clazz = Class.forName("com.tgt._02reflection._01getClass.Demo01");
System.out.println(clazz);
// Object提供的方法:: public Class getClassO;Class c3 = 对象.getClass();
Demo01 demo01 = new Demo01();
clazz = demo01.getClass();
System.out.println(clazz);
}
适用场景对比
- 方式1:已知对象实例,简单但灵活性低;
- 方式2:编译期确定类,常用于泛型或静态场景;
- 方式3:编译期无需知道类,通过字符串动态获取(如从配置文件读取类名),是框架的核心用法。
三、反射核心操作
获取 Class 对象后,就能通过反射操作类的构造器、属性、方法——这是反射的核心落地能力。
3.1 操作构造器:动态创建对象
通过反射调用构造器创建对象,支持无参和有参构造,解决“编译期无法 new 对象”的问题。
代码实现
先创建一个Dog类,里面封装着属性、方法、构造器等
public class Dog {
private String name;
private int age;
public void eat(){
System.out.println("狗吃东西...");
}
private String eat(String name){
System.out.println("狗吃" + name);
return "汪汪汪!";
}
public Dog() {
}
private Dog(String name){
this.name = name;
}
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
通过反射调用构造器创建对象
import java.lang.reflect.Constructor;
import java.util.Arrays;
public class Demo01 {
public static void main(String[] args) throws Exception {
//目标:使用反射方式动态获取指定类的构造器方法
//作用:可以通过反射灵活执行构造器创建对象
//1.获取狗的字节码对象
Class clazz = Dog.class;
//2.获取所有构造器方法对象
Constructor[] constructors = clazz.getDeclaredConstructors();
System.out.println("打印狗类中所有构造器方法:");
Arrays.stream(constructors).forEach(System.out::println);
//3.获取指定一个无参构造器 public Dog() 并调用执行创建对象
Constructor constructor = clazz.getConstructor();
Object obj1 = constructor.newInstance();
System.out.println("获取指定一个无参构造器 public Dog() 并调用执行创建对象:"+obj1);//Dog{name=null,age=0}
//4.获取指定一个私有 private Dog(String name)构造器并调用执行创建对象
Constructor constructor1 = clazz.getDeclaredConstructor(String.class);
constructor1.setAccessible(true);
Object obj2 = constructor1.newInstance("旺财");
System.out.println("获取指定一个私有 private Dog(String name)构造器并调用执行创建对象:"+obj2);////Dog{name='旺财',age=0}
//封装一个方法,传入任何类型都可以创建无参构造器对象返回(包含私有无参构造器)
//创建狗对象
Object obj3 = createObjectByClass(Dog.class);
Object obj4 = createObjectByClass(Object.class);
System.out.println(obj3);////Dog{name=null,age=0}
System.out.println(obj4);//打印内存地址
}
/**
* 通用方法:只需要传入指定class和参数,就可以创建任何对象
* @param clazz
* @return
*/
public static Object createObjectByClass(Class clazz){
try {
Constructor constructor = clazz.getConstructor();
constructor.setAccessible(true);
return constructor.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
3.2 操作属性:读写公有/私有属性
反射能突破封装,操作类的私有属性,但需谨慎使用(破坏封装)。
代码实现
引用之前的Dog类
import java.lang.reflect.Field;
import java.util.Arrays;
public class Demo02 {
public static void main(String[] args) throws Exception {
//目标:使用反射操作成员变量
//1.获取所有成员变量并打印
Class clazz = Dog.class;
Field[] fields = clazz.getDeclaredFields();
Arrays.stream(fields).forEach(System.out::println);
//2.获取private String name成员变量对象
Field field = clazz.getDeclaredField("name");
//3.给name成员变量赋值
//面向对象方式实现: 对象名.成员变量名=值
//3.1 获取对象,可以利用无参构造器创建对象
Object obj = clazz.getConstructor().newInstance();
//3.2 暴力反射
field.setAccessible(true);
//3.3 赋值
field.set(obj, "旺财");
//4.获取name成员变量的值
//面向对象方式实现: 对象名.成员变量名
Object o = field.get(obj);
System.out.println("name的值=" + o);
}
}
注意:操作私有属性/方法前,必须调用 setAccessible(true) 取消访问检查,否则抛 IllegalAccessException。
3.3 操作方法:调用公有/私有方法
反射调用方法时,需指定方法名和参数类型(避免方法重载冲突)。
代码实现
引用之前的Dog类
import java.lang.reflect.Method;
import java.util.Arrays;
public class Demo03 {
public static void main(String[] args) throws Exception {
//目标:使用反射操作成员方法
//1.获取所有成员方法并打印
Class clazz = Dog.class;
Method[] methods = clazz.getDeclaredMethods();
Arrays.stream(methods).forEach(System.out::println);
//2.获取public void eat()成员方法对象并执行
Method eat = clazz.getMethod("eat");
//面向对象执行方法: 对象名.方法名(),所以需要创建对象
Object o = clazz.getConstructor().newInstance();
eat.invoke(o);
//3.获取私有成员方法private String eat(String name) 并执行
Method eat1 = clazz.getDeclaredMethod("eat", String.class);
Object o2 = clazz.getConstructor().newInstance();
eat1.setAccessible(true);
Object o3 = eat1.invoke(o2,"骨头");
System.out.println(o3);
}
}
四、反射实战案例:简易配置化对象生成器
使用反射做一个简易版的框架
5.1 需求说明
对于任意一个对象,该框架都可以把对象的字段名和对应的值,保存到文件中去。
5.2 实现步骤
- 编写反射工具类:将任意对象数据写入磁盘文件
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.lang.reflect.Field;
public class ObjectDataWriteFileUtil {
/**
* 将任意对象数据写入磁盘文件
* @param object
*/
public static void writeObjectToFile(Object object){
try(//1.创建字符输出流操作day05-02/data.txt,采用追加输出数据
BufferedWriter writer = new BufferedWriter(new FileWriter("Reflection/data.txt",true))
) {
//2.获取obj的字节码对象Class
Class clazz = object.getClass();
//3.获取所有成员变量对象Field[]
Field[] fields = clazz.getDeclaredFields();
//4.输出“==============类名=============”
writer.write(String.format("==================%s==================%n",clazz.getSimpleName()));
//5.遍历Field[]得到每个成员变量的名称与值
for (Field field : fields) {
//6.将名称与值写入输出到文件中
writer.write(field.getName());
field.setAccessible(true);
Object o = field.get(object);
writer.write("=");
writer.write(String.valueOf(o));
writer.newLine();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
System.out.printf("当前对象%s写入磁盘成功!%n",object);
}
}
2.编写Student类和Teacher类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private int age;
private char sex;
private double height;
private String hobby;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Teacher {
private String name;
private double salary;
}
3.编写测试类
public class Demo01 {
public static void main(String[] args) {
//准备学生对象
Student student = new Student("张三", 20, '男', 1.8, "敲代码");
//准备老师对象
Teacher t = new Teacher("tgt", 1000000);
//调用反射工具类写入磁盘
ObjectDataWriteFileUtil.writeObjectToFile(student);
ObjectDataWriteFileUtil.writeObjectToFile(t);
}
}
五、总结与延伸
7.1 核心总结
- 反射入口:Class 对象( 3 种获取方式);
- 核心操作:获取并操作构造器、属性、方法;
- 关键能力:动态操作类的全部结构,是框架的核心;
- 使用原则:按需使用,避免滥用(性能差、破坏封装),高频场景缓存反射结果。