Marvel-Site Marvel-Site
首页
  • Java

    • Java基础
    • Java进阶
    • Java容器
    • Java并发编程
    • Java虚拟机
  • 计算机基础

    • 数据结构与算法
    • 计算机网络
    • 操作系统
    • Linux
  • 框架|中间件

    • Spring
    • MySQL
    • Redis
    • MQ
    • Zookeeper
    • Git
  • 架构

    • 分布式
    • 高并发
    • 高可用
    • 架构
  • 框架

    • React
    • 其他
  • 实用工具
  • 安装配置

    • Linux
    • Windows
    • Mac
  • 开发工具

    • IDEA
    • VsCode
  • 关于
  • 收藏
  • 草稿
  • 索引

    • 分类
    • 标签
    • 归档
GitHub (opens new window)

Marvel

吾必当乘此羽葆盖车
首页
  • Java

    • Java基础
    • Java进阶
    • Java容器
    • Java并发编程
    • Java虚拟机
  • 计算机基础

    • 数据结构与算法
    • 计算机网络
    • 操作系统
    • Linux
  • 框架|中间件

    • Spring
    • MySQL
    • Redis
    • MQ
    • Zookeeper
    • Git
  • 架构

    • 分布式
    • 高并发
    • 高可用
    • 架构
  • 框架

    • React
    • 其他
  • 实用工具
  • 安装配置

    • Linux
    • Windows
    • Mac
  • 开发工具

    • IDEA
    • VsCode
  • 关于
  • 收藏
  • 草稿
  • 索引

    • 分类
    • 标签
    • 归档
GitHub (opens new window)
  • Java

    • Java基础

      • Java基本概念与常识
      • Java基本语法
      • Java基本数据类型
      • Java包装类
      • Java基本概念对比辨析
      • Java面向对象基础
      • Java异常处理
      • Java注解
      • Java泛型
      • Java反射
        • 1. 反射机制概念
        • 2. 反射的应用场景
        • 3. Class类
          • 3.1 反射机制相关类
          • 3.2 理解Class对象
          • 3.3 获取Class对象
        • 4. 反射的基本操作
      • Java函数式编程
    • Java进阶

    • Java容器

    • Java并发编程

    • Java虚拟机

    • 常见面试题

  • 计算机基础

  • 框架|中间件

  • 架构

  • 后端
  • Java
  • Java基础
Marvel
2022-07-13
目录

Java反射

# Java反射

JDK8 Reflect API (opens new window)

# 1. 反射机制概念

反射机制允许程序在运行时取得任何一个已知名称 class 的内部信息,包括包括其 modifiers (修饰符),fields (属性),methods (方法)等,并可于运行时改变属性内容或调用方法。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;反射使用不当会造成很高的资源消耗!

优点 : 可以让咱们的代码更加灵活、为各种框架提供开箱即用的功能提供了便利

缺点 :让我们在运行时有了分析操作类的能力,这将增加了安全问题。比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)。另外,反射的性能也要稍差点,不过,对于框架来说实际是影响不大的。

# 2. 反射的应用场景

反射之所以被称为框架的灵魂,主要是因为它赋予了我们在运行时分析类以及执行类中方法的能力。像 Spring/Spring Boot、MyBatis 等等框架中都大量使用了反射机制和动态代理,动态代理的实现也依赖反射;

Java 中的一大利器「注解」的实现也用到了反射。

# 3. Class类

# 3.1 反射机制相关类

反射机制的相关类在 java.lang.reflect.* 包下。

与反射机制相关的重要的类

类 含义
java.lang.Class 代表整个字节码
java.lang.reflect.Method 代表字节码中的方法字节码
java.lang.reflect.Constructor 代表字节码中的构造方法字节码
java.lang.reflect.Field 代表字节码中的属性字节码

注:必须先获得Class才能获取Method、Constructor、Field

# 3.2 理解Class对象

要想理解反射,首先要理解 Class 类,因为 Class 类是实现反射的基础。

image-20220817114647776

在程序运行期间,JVM 始终为所有的对象维护一个被称为运行时的类型标识,这个信息跟踪着每个对象所属的类的完整结构信息,包括包名、类名、实现的接口、拥有的方法和字段等。可以通过专门的 Java 类访问这些信息,这个类就是 Class 类。我们可以把 Class 类理解为类的类型,一个 Class 对象,称为类的类型对象,一个 Class 对象对应一个加载到 JVM 中的一个 .class 文件。

我们编写的 .java 程序代码通过 javac 编译器编为 .class 字节码文件。类的加载过程就是 JVM 加载一个 .class 文件的过程,该过程可分为以下3步:加载、链接和初始化。

在类的加载阶段,JVM会在堆中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的访问入口。需要注意的是,每个类只有一个 Class 对象。

那么在加载完一个类后,堆内存的方法区就产生了一个 Class 对象,这个对象就包含了完整的类的结构信息,我们可以通过这个 Class 对象看到类的结构,就好比一面镜子。所以我们形象的称之为:反射。

在通常情况下,一定是先有类再有对象,我们把这个通常情况称为 “正”。那么反射中的这个 “反” 我们就可以理解为根据对象找到对象所属的类。

# 3.3 获取Class对象

从 Class 类的源码可以看出,它的构造函数是私有的,也就是说只有 JVM 可以创建 Class 类的对象,我们不能像普通类一样直接 new 一个 Class 对象。

public final class Class<T> implements java.io.Serializable, GenericDeclaration, Type, AnnotatedElement {
    ...
    /*
     * 私有化构造器,只能由JVM创建Class对象,这个构造不会被使用,它会防止空参构造器的自动生成
     */
    private Class(ClassLoader loader) {
        classLoader = loader;
    }
1
2
3
4
5
6
7
8

我们只能通过已有的类来得到一个 Class类对象,Java 提供了四种方式:

  1. 知道具体类
Class clazz = TargetObject.class;
1
  1. 通过 Class.forName()传入类的全路径获取
Class clazz = Class.forName("com.xxx.TargetObject");
1
  1. 通过对象实例 instance.getClass() 获取
TargetObject o = new TargetObject();
Class clazz = o.getClass();
1
2
  1. 通过类加载器 xxxClassLoader.loadClass() 传入类路径获取
Class clazz = ClassLoader.loadClass("com.xx.TargetObject");
1

注意:通过类加载器获取 Class 对象不会进行初始化,意味着不进行包括初始化等一系列步骤,静态代码块和静态对象不会得到执行

# 4. 反射的基本操作

创建一个我们要使用反射操作的类 TargetObject

package com.zqc;

public class TargetObject {
    private String value;

    public TargetObject() {
        value = "Reflect";
    }

    public void publicMethod(String s) {
        System.out.println(s);
    }

    private void privateMethod() {
        System.out.println("value is " + value);
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

使用反射操作这个类的方法以及参数

package com.zqc;

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

public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException {
        /*
         * 获取 TargetObject 类的 Class 对象并且创建 TargetObject 类实例
         */
        Class<?> targetClass = Class.forName("com.zqc.TargetObject");
        TargetObject targetObject = (TargetObject) targetClass.newInstance();

        /*
         * 获取 TargetObject 类中定义的所有方法
         */
        Method[] methods = targetClass.getDeclaredMethods();
        System.out.println("-----------获取类中的所有方法-----------");
        for (Method method : methods) {
            System.out.println(method.getName());
        }

        /*
         * 获取指定方法并调用
         */
        System.out.println("-----------调用publicMethod-----------");
        Method publicMethod = targetClass.getDeclaredMethod("publicMethod", String.class);
        publicMethod.invoke(targetObject, "publicTest");

        /*
         * 获取指定参数并对参数进行修改
         */
        Field field = targetClass.getDeclaredField("value");
        // 为了对类中的参数进行修改我们取消安全检查
        field.setAccessible(true);
        field.set(targetObject, "Java");

        /*
         * 调用 private 方法
         */
        System.out.println("-----------调用privateMethod-----------");
        Method privateMethod = targetClass.getDeclaredMethod("privateMethod");
        // 为了调用private方法我们取消安全检查
        // 默认false,设置为true为打破封装
        privateMethod.setAccessible(true);
        privateMethod.invoke(targetObject);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

控制台输出:

-----------获取类中的所有方法-----------
privateMethod
publicMethod
-----------调用publicMethod-----------
publicTest
-----------调用privateMethod-----------
value is Java
1
2
3
4
5
6
7
编辑 (opens new window)
#Java
上次更新: 2023/08/20, 21:21:52
Java泛型
Java函数式编程

← Java泛型 Java函数式编程→

最近更新
01
位运算
05-21
02
二叉树
05-12
03
Spring三级缓存解决循环依赖
03-25
更多文章>
Theme by Vdoing | Copyright © 2022-2024 Marvel
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式