反射机制

反射机制

:::warning

  • 反射机制是非常难解释清楚的,主要原因有如下几个
    • 反射机制概念本身就很抽象,难以理解其概念
    • 反射机制在初学时使用的机会很少,因为初学者甚至都没有类加载的概念
    • 反射机制涉及到虚拟机中类加载的概念,在各个框架中也有很广泛的应用,没有这些的基本了解真的很难理解
  • 我会尽可能将反射最基础的内容和虚拟机联系起来

:::

:::primary

参考博客:Java基础篇:反射机制详解

:::

什么是类加载机制?

:::info

详细了解:Java 虚拟机-类加载机制

:::

什么是反射?

  • 定义:

    • [虚拟机处于 运行状态 中]{.red}
    • [对于任意一个类都能够知道这个类所有的属性和方法]{.blue}
    • [对于任意一个对象,都能够调用它的任意一个方法]{.blue}
  • 核心:[在进程运行期间将类 动态 地加载到虚拟机内存中使用]{.red}

为什么要使用反射?

如何使用反射?

反射的原理是什么?

反射

  • 概念解释

    • Java 程序处于运行过程中
    • 根据 Java 程序的需要
    • 动态加载 .class 文件
    • 图示解释
  • 目的解释

    • 更加灵活地根据需求加载 .class 文件

    例:每个数据库系统都有不同的数据库驱动(MySQL Oracle);

    如果在编写代码的时候无法确定程序在执行的过程中究竟使用哪一套数据库驱动系统;

    采用反射机制使用 Class.forName(需要加载的类的全限定名)加载需要使用的数据库驱动

  • Java 成为准动态语言

@CallerSensitive
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
{
Class<?> caller = null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// Reflective call to get caller class is only needed if a security manager
// is present. Avoid the overhead of making this call otherwise.
caller = Reflection.getCallerClass();
if (sun.misc.VM.isSystemDomainLoader(loader)) {
ClassLoader ccl = ClassLoader.getClassLoader(caller);
if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
sm.checkPermission(
SecurityConstants.GET_CLASSLOADER_PERMISSION);
}
}
}
// forName 方法会自动选择类加载器(ClassLoader)加载.class文件
return forName0(name, initialize, loader, caller);
}

反射的用途

  • 创建实例
  • 框架实现的原理

反射主要内容

Javac

  • 基本概念:Javac 全称 Java Compiler

  • 目的

    • Java 源代码实是由 Javac.exe 执行的

      也就是说我们执行 Java 程序实际上是在执行 Javac.exe

    • Javac 将由 Java 语言编写的源代码 转换成 机器能够识别的 字节码文件(.class)

      ![image-20210307172652963](D:\Typora\Typora\文件\Java 反射机制.assets\image-20210307172652963.png)

  • 使用

类加载器

  • 概念:将 C++ 和 Java 编写完成的 “类” 加载到堆中的方法区中生成 Class 对象

    • 类:所有的类(.java)文件都被 Javac 编译成 .class 文件;所以实际类加载器加载的是 .class 文件
  • 分类

    • BootstrapClassLoader
      • 功能:负责加载 Java 语言中的最核心的类库(java.lang.*) 和 构造 ExtClassLoader 和 APPClassLoader
      • 特点
        • C++编写
        • 涉及到虚拟机的本地实现细节,不能够被直接调用
    • ExtClassLoader
      • 功能:负责加载 Java 语言中的扩展类库(javax.*)
      • 特点
        • Java 编写
        • 开发者可以实际调用
    • APPClassLoader
      • 功能:主要负责加载应用程序的主函数类
  • 类加载机制

    • 图示

      ![preview](D:\Typora\Typora\文件\Java 反射机制.assets\v2-b9d39568c0e3f87a5df6a0cbfe753cda_r.jpg)

  • 双亲委派机制

    • 图示

      ![image-20210307175110119](D:\Typora\Typora\文件\Java 反射机制.assets\image-20210307175110119.png)

    • 解释

      自定义类加载器 开始判断当前的类是否加载过;

      如果当前的类已经在任何一个 类加载器中加载过了,那么当前类就不需要加载了

      如果直到 核心类库加载器 中还没有加载过,那么开始从 核心类库加载器 开始判断当前类是否可以被加载

      如果当前的类加载器无法加载那么依次向下提交,直到 自定义类加载器

      如果连 自定义类加载器 都无法加载,那么证明当前的类是无法被加载的

    • 目的

      • 避免类的重复加载
      • 避免基础核心类库被篡改(自行编写的 String 类是无法被加载的)

双亲委派机制1

双亲委派机制2

实例构建过程

![image-20210307180421814](D:\Typora\Typora\文件\Java 反射机制.assets\image-20210307180421814.png)

Class 对象

  • 概念:.class 文件被类加载器载入堆的方法区中生成 Class 对象
  • 内容
    • Class 对象具有被加载类的所有信息(注解,方法,字段,构造函数等)
    • Class 对象提供 Method Field Constructor 静态类存储方法字段构造器的所有信息
  • 源代码
private static class ReflectionData<T> {
volatile Field[] declaredFields; // 字段和方法的信息太多,所以Class对象提供专门的静态类用于存储信息
volatile Field[] publicFields;
volatile Method[] declaredMethods;
volatile Method[] publicMethods;
volatile Constructor<T>[] declaredConstructors;
volatile Constructor<T>[] publicConstructors;
// Intermediate results for getFields and getMethods
volatile Field[] declaredPublicFields;
volatile Method[] declaredPublicMethods;
volatile Class<?>[] interfaces;

// Value of classRedefinedCount when we created this ReflectionData instance
final int redefinedCount;

ReflectionData(int redefinedCount) {
this.redefinedCount = redefinedCount;
}
}

API

  • 获取类的方式
// 第一种方式 -- 任意情况都能够使用
Class clz = Class.forName("java.lang.String");
// 第二种方式 -- 明确知道类的使用方法
Class clz = String.class;
// 第三种方式
String str = new String("Hello");
Class clz = str.getClass();
  • 获得构造器的方式
Constructor getConstructor(Class[] params) -- 获得使用特殊的参数类型的公共构造函数, 

Constructor[] getConstructors() -- 获得类的所有公共构造函数

Constructor getDeclaredConstructor(Class[] params) -- 获得使用特定参数类型的构造函数(与接入级别无关)

Constructor[] getDeclaredConstructors() -- 获得类的所有构造函数(与接入级别无关)
  • 获得字段的方式
Field getField(String name) -- 获得命名的公共字段 

Field[] getFields() -- 获得类的所有公共字段

Field getDeclaredField(String name) -- 获得类声明的命名的字段

Field[] getDeclaredFields() -- 获得类声明的所有字段
  • 获得方法的方式
Method getMethod(String name, Class[] params) -- 使用特定的参数类型,获得命名的公共方法 

Method[] getMethods() -- 获得类的所有公共方法

Method getDeclaredMethod(String name, Class[] params) -- 使用特写的参数类型,获得类声明的命名的方法
/*
这里必须要传入 参数的类型 而且只能够是 Class 对象
不能够只传入方法的名称,这样是无法找到方法的(JVM实际上是在Class对象中循环遍历找到的)
重载的实现过程实际上是 JVM 在执行方法的时候将参数都传入进去了,所以反射获取方法的时候同样需要传递参数
*/

Method[] getDeclaredMethods() -- 获得类声明的所有方法

Java 反射机制

Java反射 API

Author: Fuyusakaiori
Link: http://example.com/2021/09/22/java/reflection/反射机制/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.