当前位置: 首页 > news >正文

Java反射完全指南:从入门到精通

🎯 适合人群:Java小白到进阶开发者
🧭 学习目标:全面理解 Class、反射获取类信息/字段/方法/构造、类加载器、常见异常与最佳实践
⏱️ 阅读时长:约30-40分钟


🧠 为什么要学反射?

反射(Reflection)让我们在“运行时”动态地检查和操作类的结构:查询类名、父类、接口,读取/设置字段值,调用方法,构造对象。它是很多框架(Spring、ORM、序列化、依赖注入)背后的基石。

记住两件事:

  • 反射是“动态”的(运行时),能做编译期做不到的事;
  • 功能强、开销也更高,用在“需要动态性”的地方(如配置驱动、框架内部)。

🔤 反射与 Class:拿到“类的镜子”

获取 Class 的三种方式:

// 1) 通过类名 .class(编译期已知类型)
Class<Student> c1 = Student.class;
// 2) 通过对象实例 .getClass()
Student s = new Student();
Class<?> c2 = s.getClass();
// 3) 通过全限定名 Class.forName(运行时按名称加载)
Class<?> c3 = Class.forName("chapter10.Student");

区别:Class.forName 会触发类加载(可能执行静态代码块),而 .class/getClass() 通常不会立即初始化。


🧱 读取类的基本信息(基于 chapter10 示例)

示例参考 Java01_Reflect.java

User user = new Child();
Class<? extends User> aClass = user.getClass();
System.out.println(aClass.getName());         // 包+类名
System.out.println(aClass.getSimpleName());   // 简单类名
System.out.println(aClass.getPackageName());  // 包名Class<?> superClass = aClass.getSuperclass(); // 父类
Class<?>[] interfaces = aClass.getInterfaces(); // 接口数组int modifiers = aClass.getModifiers();        // 修饰符位掩码
boolean isPrivate = java.lang.reflect.Modifier.isPrivate(modifiers);

注意:示例中出现的 aClass.getField("xxxxx")getDeclaredMethod("xxxx") 只是占位写法,真实运行会 NoSuchFieldException/NoSuchMethodException。下文给出正确示例。


🧩 字段 / 方法 / 构造 的获取与使用

字段(Field):

// 仅 public 字段(含父类)
Field[] publicFields = aClass.getFields();
// 所有声明的字段(不含父类)
Field[] allFields = aClass.getDeclaredFields();// 单个字段
Field f = aClass.getDeclaredField("account"); // 可访问私有字段
f.setAccessible(true);                        // 关闭权限检查(有风险,需受信任环境)
Object value = f.get(obj);
f.set(obj, "admin");

方法(Method):

// 仅 public 方法(含父类)
Method[] publicMethods = aClass.getMethods();
// 所有声明的方法(不含父类)
Method[] declaredMethods = aClass.getDeclaredMethods();// 单个无参方法
Method m1 = aClass.getMethod("login");
Object r1 = m1.invoke(obj);// 单个有参方法
Method m2 = aClass.getMethod("setAge", int.class);
m2.invoke(obj, 18);

构造(Constructor):

// 仅 public 构造器
Constructor<?>[] publicCtors = aClass.getConstructors();
// 所有声明的构造器
Constructor<?>[] allCtors = aClass.getDeclaredConstructors();// 无参构造
Constructor<?> ctor0 = aClass.getDeclaredConstructor();
ctor0.setAccessible(true);
Object obj = ctor0.newInstance();// 有参构造
Constructor<?> ctor = aClass.getDeclaredConstructor(String.class, int.class);
Object obj2 = ctor.newInstance("Tom", 18);

🧪 实战:登录小练习(修正版)

参考 Java03_Reflect_Test.java,原代码通过反射设值并调用 login()

Class<?> empClass = Emp.class;
Constructor<?> ctor = empClass.getDeclaredConstructor();
Object emp = ctor.newInstance();Field account = empClass.getField("account");   // 这里字段是 public,getField 可用
Field password = empClass.getField("password");
account.set(emp, "admin");
password.set(emp, "admin");Method login = empClass.getMethod("login");
Object result = login.invoke(emp);
System.out.println(result); // true / false

如果字段是私有:

Field account = empClass.getDeclaredField("account");
account.setAccessible(true);
account.set(emp, "admin");

🧰 类加载器(ClassLoader)与“谁来加载类”

参考 Java02_Reflect_ClassLoader.java

Class<Student> studentClass = Student.class;
ClassLoader cl = studentClass.getClassLoader();
System.out.println(cl);                 // AppClassLoader(应用类加载器)
System.out.println(cl.getParent());     // PlatformClassLoader(平台类加载器)
System.out.println(cl.getParent().getParent()); // 可能为 null(引导类加载器用 C/C++ 实现,不是 Java 对象)Class<String> stringClass = String.class;
System.out.println(stringClass.getClassLoader()); // 一般是 null,表示由引导类加载器加载

类加载器常见“三层”模型:

  • BootstrapClassLoader(引导/启动类加载器):加载核心类库(java.*),在 Java 代码中表现为 null
  • PlatformClassLoader(平台类加载器):加载 Java 平台模块。
  • AppClassLoader(应用类加载器):加载应用 classpath 下的类(我们写的类)。

口诀:核心类库(null)→ 平台 → 应用。输出的第三级通常是 null,不是异常。


🚨 常见异常与避坑(基于 Java04_Reflect_Exception.java

原文件中有一段:

Class<?> empClass = Class.forName("chapter10.Emp");

但同文件只定义了 class Emp4,这会导致 ClassNotFoundException。应改为:

Class<?> empClass = Class.forName("chapter10.Emp4");

同时,注释中的异常解释更正:

  • ClassNotFoundException:类找不到(Class.forName 指向的类名不对 / 未在类路径)。
  • NoSuchMethodException:找不到指定方法。
  • IllegalArgumentException:传入方法/构造的参数不合法。
  • NoSuchFieldException:找不到指定字段(原注释“没有顺序的异常”应为“没有字段”)。
  • IllegalAccessException:访问受限(未 setAccessible(true) 等)。
  • InvocationTargetException:反射调用方法时,方法内部抛出了异常,会被包裹到此异常中。

完整可运行修正版片段:

Class<?> empClass = Class.forName("chapter10.Emp4");
Constructor<?> ctor = empClass.getDeclaredConstructor();
Object emp = ctor.newInstance();Field account = empClass.getField("account");
Field password = empClass.getField("password");
account.set(emp, "admin");
password.set(emp, "admin");Method login = empClass.getMethod("login");
Object result = login.invoke(emp);
System.out.println(result);

🧭 最佳实践速记

  • 尽量优先常规调用,反射用于“确有动态需求”时。
  • 使用 getDeclaredXxx 可获取私有成员,需 setAccessible(true),并确保受信任环境。
  • 通过 getMethods()/getFields() 只能拿到 public(含父类),要拿本类私有成员用 getDeclaredXxx
  • Class.forName 需要全限定名,且可能触发初始化(注意副作用与性能)。
  • 反射有开销,涉及热点路径时可做缓存(例如缓存 Method/Constructor 对象)。

🧪 进阶加餐:动态调用有参方法与私有方法

class Person {private String name = "Tom";private int age = 18;private String secret(String prefix) { return prefix + name + ":" + age; }
}public class InvokeDemo {public static void main(String[] args) throws Exception {Class<?> c = Person.class;Object p = c.getDeclaredConstructor().newInstance();// 访问/修改私有字段Field f = c.getDeclaredField("name");f.setAccessible(true);System.out.println("old: " + f.get(p));f.set(p, "Jerry");// 调用私有有参方法Method m = c.getDeclaredMethod("secret", String.class);m.setAccessible(true);Object ret = m.invoke(p, ">> ");System.out.println(ret); // >> Jerry:18}
}

🎯 面试常见问题与答案

1)getFieldgetDeclaredField 区别?

  • getField 仅返回 public 字段(包括父类);getDeclaredField 返回“当前类声明的”字段(可含 private,不含父类)。方法与构造器也同理。

2)Class.forNameSomeClass.class 的区别?

  • Class.forName 按名称加载类,通常会触发类初始化;.class 是编译期可得的类字面量,不会主动触发初始化(取决于使用方式)。

3)为什么 String.class.getClassLoader() 往往是 null

  • 因为 String 属于核心类库,由引导类加载器加载;引导类加载器不是 Java 对象,在 Java 中表现为 null

4)如何通过反射调用私有方法?安全性如何?

  • 通过 getDeclaredMethod + setAccessible(true),仅应在受信任环境使用(可能破坏封装,存在安全风险)。

5)反射有什么性能问题?如何缓解?

  • 反射绕过了JIT优化的一些路径,方法查找/检查也有成本。可通过缓存 Method/Field/Constructor、减少反射调用频率来优化。

6)InvocationTargetException 何时出现?

  • 当被反射调用的方法内部抛出异常时,JVM 会把原始异常包在 InvocationTargetException 中抛出;需通过 getCause() 获取根因。

7)能用反射实例化没有无参构造的类吗?

  • 可以:获取有参构造 getDeclaredConstructor(参数类型...),再 newInstance(参数...)。必要时 setAccessible(true)

✅ 小结

  • 反射是“运行时”查看和操作类结构的能力,是 Java 框架的基石。
  • 熟练掌握 Class 的获取、字段/方法/构造的反射用法,了解类加载器分层与常见异常。
  • 反射应“按需使用”,注意性能与安全。

如果本文对你有帮助,欢迎点赞、收藏、评论交流!有任何疑问或想看的反射高级案例(例如注解解析、包扫描、类路径发现、动态代理),留言告诉我~

http://www.dtcms.com/a/598029.html

相关文章:

  • seo网站推广报价企业系统包括哪些系统
  • 冬日暖居:科学应对暖气病的生活哲学
  • 网站建设的7个基本流程网站下面版权代码
  • 从文字到世界:空间智能是人工智能的下一个前沿
  • 51单片机逆向
  • 网站域名的管理密码如何索取wordpress禁止加载头部
  • Dubbo-学习笔记1
  • 怎样建设个自己的网站首页中信建设有限责任公司电话打不通
  • 无人机信息采集模块技术要点与难点
  • DDR4 4.27 Self refresh Operation
  • 建设工程业绩查询网站专业制作网站有哪些
  • 新闻类网站开发难点wordpress 注册填密码
  • 金耀网站建设秦皇岛建设局长
  • 捕鱼网站开发自学ui设计学什么软件
  • 网站的建设方法有哪些番禺区网站建设公司
  • 响应式编程 | 如何通过响应式编程提升前端开发效率
  • 网站建设北京海淀seo顾问服
  • 网站空间150m游戏推广话术
  • 高端大气的科技网站网站建设专业的公司
  • 蒙文网站建设情况汇报怎么看网站是服务器还是虚拟主机
  • 淘宝网站建设教程视频温岭网站建设制作
  • CANN中MmDeqSwigluQuantMmDeq算子模型的深层解析
  • 03.Python语言中的变量
  • 攻防世界-Misc-can_has_stdio?
  • 新余网站开发公司巩义网站建设方案表
  • 东莞网络网站建设为什么运行wordpress
  • 【赵渝强老师】MySQL集群解决方案
  • CKA07--Argo CD
  • 事务隔离级别
  • html5网站开发软件重庆网络公司招聘