第28篇:Java反射机制原理详解


📌 系列导航《Java 100 天进阶之路》完整目录 |
⬅️ 上一篇:第27篇:Java代理类详解 |
➡️ 下一篇:第29篇:Java Date类详解


一、核心知识点

  • 反射的核心能力:运行时获取类信息、创建对象、调用方法、访问字段(包括私有)
  • 获得 Class 对象的三种方式:Class.forName()对象.getClass()类名.class
  • 核心 API:ClassMethodFieldConstructor
  • 反射的优缺点:灵活、动态 vs 性能开销、破坏封装、安全隐患
  • 反射的应用:框架(Spring、MyBatis)、注解处理、动态代理、序列化

二、通俗讲解(1分钟开心学)

1. 什么是反射?

反射就是在程序运行时,动态地获取类的信息(有哪些方法、字段、构造器),并且可以调用它们,甚至访问私有成员。就好像你在黑暗中用手电筒照一个物体,能看清它的所有细节。

2. 获得 Class 对象的三种方式

  • Class.forName("全限定类名"):最常用,会触发类初始化。
  • 对象.getClass():已经有实例时使用。
  • 类名.class:编译时已知类型,最直接,不触发初始化。

3. 反射能做什么?

  • 创建实例(替代 new
  • 调用方法(包括私有方法)
  • 读写字段(包括私有字段)
  • 获取注解信息
  • 获取泛型类型信息

4. 反射的代价

  • 性能比直接调用稍差(但现代 JVM 有优化)
  • 破坏封装性,可能绕过权限检查
  • 代码可读性下降,容易出错

生活类比
正常编程就像拿着说明书组装家具(编译时已知)。反射就像你拿到一个没拆封的快递(运行时才知道里面是什么),你得用工具把它拆开,看清里面所有零件,再组装。

三、实操代码案例 + 场景说明

场景:写一个通用的 toString 工具,可以打印任意对象的字段值。

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectionDemo {
    public static void main(String[] args) throws Exception {
        // 1. 获取Class对象的三种方式
        Class<?> clazz1 = Class.forName("java.lang.String");
        Class<?> clazz2 = "hello".getClass();
        Class<?> clazz3 = String.class;
        System.out.println(clazz1 == clazz2); // true
        
        // 2. 创建对象(调用无参构造)
        Class<?> personClazz = Person.class;
        Person p = (Person) personClazz.getDeclaredConstructor().newInstance();
        p.setName("张三");
        
        // 3. 调用私有方法
        Method privateMethod = personClazz.getDeclaredMethod("secret", String.class);
        privateMethod.setAccessible(true);  // 绕过访问检查
        privateMethod.invoke(p, "密码123");
        
        // 4. 修改私有字段
        Field nameField = personClazz.getDeclaredField("name");
        nameField.setAccessible(true);
        nameField.set(p, "李四");
        System.out.println(p.getName()); // 李四
        
        // 5. 遍历所有字段(通用toString)
        System.out.println(objectToString(p));
    }
    
    // 通用toString:打印对象所有字段名和值
    public static String objectToString(Object obj) throws IllegalAccessException {
        StringBuilder sb = new StringBuilder();
        Class<?> clazz = obj.getClass();
        sb.append(clazz.getSimpleName()).append("{");
        Field[] fields = clazz.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            fields[i].setAccessible(true);
            sb.append(fields[i].getName()).append("=").append(fields[i].get(obj));
            if (i < fields.length - 1) sb.append(", ");
        }
        sb.append("}");
        return sb.toString();
    }
}

class Person {
    private String name;
    public void setName(String name) { this.name = name; }
    public String getName() { return name; }
    private void secret(String key) {
        System.out.println("私有方法被调用,key=" + key);
    }
}

四、避坑要点

错误/误区 后果 正确做法
频繁调用反射(如循环中) 性能下降 缓存 MethodField 对象
忘记 setAccessible(true) IllegalAccessException 访问私有成员前必须设置
Class.forName()ClassLoader.loadClass() 混淆 前者会初始化静态块,后者不会 根据需求选择
反射调用方法参数类型不匹配 IllegalArgumentException 确保参数类型完全匹配(包括基本类型 vs 包装类)

五、面试高频考点

Q1:反射的优缺点?

优点:动态性,框架基础,提高代码灵活性。缺点:性能略低,破坏封装,存在安全隐患(可访问私有数据)。

Q2:getMethodgetDeclaredMethod 的区别?

getMethod 获取 public 方法(包括继承的);getDeclaredMethod 获取本类声明的所有访问级别的方法(不包括继承)。

Q3:反射可以获取泛型参数化类型信息吗?

可以。通过 Method.getGenericParameterTypes() 等获取 Type 对象,可得到实际类型参数(如果编译时保留了泛型信息)。

六、练习题

  1. 动手:写一个方法,接收一个对象,打印该对象的所有方法名称(包括私有)。
  2. 代码分析:利用反射创建 ArrayList<Integer> 实例,并添加元素,再通过反射遍历输出。
  3. 简答:Spring 框架中哪些地方使用了反射?

📊 你的学习进度

  • 当前:第28篇 / 共44篇 · 第四阶段:注解、反射、代理、日期(第26~31篇)
  • ✅ 已完成:第1~27篇
  • 📖 正在学:第28篇
  • ⏳ 待学习:第29~44篇

👉 📚 完整目录 & 学习指南 | 🔥 订阅本专栏,不错过每一篇

💡 本专栏每篇都包含:避坑表 + 面试高频考点 + 练习题。每天30分钟,100天拿offer!


下一篇文章预告

《Java Date 类详解》

内容简介:Date类的缺陷、SimpleDateFormat线程不安全、java.time包(LocalDate/LocalDateTime/DateTimeFormatter)。

💡 学完这篇,你将彻底告别旧版日期API的坑,写出线程安全的日期处理代码。

📌 《Java 100 天进阶之路 | 从入门到上岗就业》 每天一篇,建议收藏 + 关注,一起100天拿offer!
👉 点击关注我,更新后第一时间收到推送!


更多推荐