Java反射机制讲解,利用疑问一步步刨析

发布于:2025-06-28 ⋅ 阅读:(21) ⋅ 点赞:(0)

大家好,我是爱骑行的Coder。
今天来通过的疑问的方式来学习反射,这里的疑问步骤也是当时我学习时内心最真实的疑惑链路。

什么是反射?
反射就是程序在运行时可以查看、操作自己的属性和方法。
即使不知道类具体是谁,也可以动态地:

  • 找到这个类
  • 创建这个类的对象
  • 调用它的方法
  • 查看和修改它的属性

为什么要用反射?
需要写灵活、通用的代码时,通常会用反射:

  1. 不知道具体要加载哪个类时(比如根据配置文件的内容,动态决定加载哪个类)
  2. 需要根据类名字符串动态地创建对象、调用方法时(比如框架内部根据字符串名字调用方法,比如Spring)

反射如何实现?

Java提供了 java.lang.reflect 包,里面的几个重要类:

  • Class :表示类的字节码文件,类的描述信息
  • Field :表示类的属性
  • Method :表示类的方法
  • Constructor :表示类的构造方法

通过这些类,我们可以做到:

  • 获取对象的字段、方法
  • 调用方法、访问和修改字段
  • 创建新对象实例
// 1. 获取 Class 对象
//JVM底层:forName 方法调用后,JVM 会根据类名 "Person" 去 方法区 查找该类的 类元数据(Class对象)
Class<?> clazz = Class.forName("Person"); // 注意类名是字符串!

// 2. 创建对象(调用无参构造函数)
//JVM底层:getConstructor() 方法从 Class 对象(方法区中)获取 无参构造方法。
//由于构造方法属于 类的元数据,因此这些信息保存在 方法区 中。
Constructor<?> constructor = clazz.getConstructor();
Object personObject = constructor.newInstance();

// 3. 调用方法 sayHello
Method sayHelloMethod = clazz.getMethod("sayHello");
sayHelloMethod.invoke(personObject);

// 4. 修改 private 属性 name
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true); // 因为 name 是 private,需要暴力反射
nameField.set(personObject, "Reflection User");

// 5. 再次调用 sayHello
sayHelloMethod.invoke(personObject);
Class对象和.class文件是什么关系?
- .class 文件是字节码文件,存在磁盘上(文件系统中)。
- Class 对象是JVM内存中,表示这个类结构的一个描述信息。

总结一句话:
.class 是磁盘上的文件,Class是内存里的对象。

通过 .class 文件被加载进JVM后,就会生成对应的 Class 对象。

通过 Class.forName 和 反射创建对象的区别?

  • Class.forName(“com.example.Person”) 是找到类对应的Class对象,它描述了这个类的结构。
  • clazz.getConstructor().newInstance() 是真正地根据Class对象创建一个新的实例对象。

JVM在调用Class.forName()时,如何找到对应的类?

  • Class.forName() 只传了一个字符串,比如 “com.example.User”。 这个就是全限定名(Fully Qualified Name)
  • JVM会用类加载器(ClassLoader)来根据这个字符串去定位字节码文件(.class)。
  • 找到了 .class 文件,读取并加载进内存,生成对应的 Class 对象。

类加载器(ClassLoader)

什么是类加载器?

类加载器是负责把.class文件读进内存并生成Class对象的特殊对象。
ClassLoader本身是一个类。
每个具体的加载器,比如A加载器、B加载器,都是ClassLoader类的实例(对象)。


不同加载器加载同一个class,会有什么不同?
即使加载的是同一个User.class文件,但是如果用不同的加载器A、B来加载,JVM会认为它们是两个不同的类,两个不同的Class对象。
在JVM看来,类是由”类加载器+类名”唯一确定的,不是单靠类名!
所以,不同ClassLoader加载的类互不兼容。


加载器A、B和普通对象有什么区别?

  • ClassLoader 是一个类,定义了加载类的能力
  • A、B 是ClassLoader类的对象(实例)
  • A、B加载出来的Class对象是不同的
  • 这些Class对象互相之间不兼容

双亲委派模型

什么是双亲委派模型?

双亲委派:
当一个类加载器加载类时,它不会自己直接去加载,而是:

  1. 先交给父类加载器去加载
  2. 父类找到了,就直接用父类的结果
  3. 父类找不到,子类加载器才自己加载

这种方式叫做“委托父亲”,所以叫“双亲委派”。


双亲委派模型的作用?

  1. 保证核心Java类优先加载
    防止有人伪造系统类,比如伪造 java.lang.String
  2. 避免重复加载,提高效率
    父类加载器已经加载的类,子类就不需要再加载
  3. 保证类型的一致性
    防止同一个名字的类被不同版本加载,引起冲突

网站公告

今日签到

点亮在社区的每一天
去签到