IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java中的反射与动态代理 -> 正文阅读

[Java知识库]Java中的反射与动态代理

前言

一直以来反射技术都是 Java 中的闪亮点,这也是目前大部分框架(如 Spring/Mybatis 等)得以实现的支柱。在 Java 中,Class 类与 java.lang.reflect 类库一起对反射技术进行了全力的支持

反射的概述

反射的概念

Java 反射机制是在 运行状态 中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制

反射之中包含了一个「反」字,所以想要解释反射就必须先从「正」开始解释。一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作

Apple apple = new Apple(); //直接初始化,「正射」
apple.setPrice(4);

上面这样子进行类对象的初始化,我们可以理解为「正」。而反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。这时候,我们使用 JDK 提供的反射 API 进行反射调用

Class clz = Class.forName("com.chenshuyi.reflect.Apple");
Method method = clz.getMethod("setPrice", int.class);
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object, 4);

上面两段代码的执行结果,其实是完全一样的。但是其思路完全不一样,第一段代码在未运行时就已经确定了要运行的类(Apple),而第二段代码则是在运行时通过字符串值才得知要运行的类(com.chenshuyi.reflect.Apple

反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法

反射的前提

要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是 Class 类中的方法。所以先要获取到每一个字节码文件对应的 Class 类型的对象

反射的使用

Java 中,Class 类与 java.lang.reflect 类库一起对反射技术进行了全力的支持。在反射包中,我们常用的类主要有 Constructor 类表示的是 Class 对象所表示的类的构造方法,利用它可以在运行时动态创建对象、Field 表示 Class 对象所表示的类的成员变量,通过它可以在运行时动态修改成员变量的属性值(包含 private)、Method 表示 Class 对象所表示的类的成员方法,通过它可以动态调用对象的方法(包含 private),下面将对这几个重要类进行分别说明

Class

如图是类的正常加载过程:反射的原理在与 Class 对象。熟悉一下加载的时候:Class 对象的由来是将 Class 文件读入内存,并为之创建一个 Class 对象

在这里插入图片描述

  • Class 类的实例表示正在运行的 Java 应用程序中的类和接口。也就是 JVM 中有 N 多的实例每个类都有其 Class 对象(包括基本数据类型)
  • Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM 已经帮我们创建好了

没有公共的构造方法,方法共有 64 个太多了。下面用到哪个就详解哪个吧

在这里插入图片描述

获取 Class 对象

  • Object ——> getClass();
  • 任何数据类型(包括基本数据类型)都有一个静态的 Class 属性
  • 通过Class类的静态方法:forName(String className)

第一个是因为 Object 类中的 getClass 方法,因为所有类都继承 Object 类。从而调用 Object 类来获取

在这里插入图片描述

public class ReflexTest {

    public static void main(String[] args) {
        /*第一种方式获取Class对象*/
        Student student = new Student();/*这一new产个生一Student对象,一个Class对象*/
        Class<? extends Student> studentClass1 = student.getClass();
        System.out.println(studentClass1.getName());

        /*第二种方式获取Class对象*/
        Class<Student> studentClass2 = Student.class;
        System.out.println(studentClass1 == studentClass2);

        /*第三种方式获取Class对象*/
        try {
            /*注意此字符串必须是真实路径,就是带包名的类路径,包名.类名*/
            Class<?> studentClass3 = Class.forName("org.dabao.reflex.Student");
            System.out.println(studentClass2 == studentClass3);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}


结果:
org.dabao.reflex.Student
true
true

注意:在运行期间,一个类,只有一个 Class 对象产生。三种方式常用第三种,第一种对象都有了还要反射干什么。第二种需要导入类的包,依赖太强,不导包就抛编译错误。一般都第三种,一个字符串可以传入也可写在配置文件中等多种方法

Constructor

Constructor 类存在于反射包 java.lang.reflect 中,反映的是 Class 对象所表示的类的构造方法。获取 Constructor 对象是通过 Class 类中的方法获取的,Class 类与 Constructor 相关的主要方法如下

方法返回值方法名称方法说明
static Class<?>forName(String className)<、code>返回与带有给定字符串名的类或接口相关联的 Class 对象
Constructor<T>getConstructor(Class<?>... parameterTypes)返回指定参数类型、具有public访问权限的构造函数对象
Constructor<?>[]getConstructors()返回所有具有public访问权限的构造函数的Constructor对象数组
Constructor<T>getDeclaredConstructor(Class<?>... parameterTypes)返回指定参数类型、所有声明的(包括private)构造函数对象
Constructor<?>[]getDeclaredConstructor()返回所有声明的(包括private)构造函数对象
TnewInstance()创建此 Class 对象所表示的类的一个新实例

其他方法请查看 API

Field

Field 类存在于反射包 java.lang.reflect 中,它提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。同样的道理,我们可以通过 Class 类的提供的方法来获取代表字段信息的 Field 对象,Class 类与 Field 对象相关方法如下

方法返回值方法名称方法说明
FieldgetDeclaredField(String name)获取指定name名称的(包含private修饰的)字段,不包括继承的字段
Field[]getDeclaredField()获取Class对象所表示的类或接口的所有(包含private修饰的)字段,不包括继承的字段
FieldgetField(String name)获取指定name名称、具有public修饰的字段,包含继承字段
Field[]getField()获取修饰符为public的字段,包含继承字段

其他方法请查看 API

Method

Method 类存在于反射包 java.lang.reflect 中,它提供关于类或接口上单独某个方法(以及如何访问该方法)的信息,所反映的方法可能是类方法或实例方法(包括抽象方法)。下面是 Class 类获取 Method 对象相关的方法

方法返回值方法名称方法说明
MethodgetDeclaredMethod(String name, Class<?>... parameterTypes)返回一个指定参数的Method对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法
Method[]getDeclaredMethod()返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法
MethodgetMethod(String name, Class<?>... parameterTypes)返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法
Method[]getMethods()返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法

反射与动态代理

我们知道 spring 主要有两大思想,一个是 IOC,一个就是 AOP。对于 IOC 容器中bean 的创建就是使用了 Java 的反射技术;而对于 spring 的核心 AOP 来说,我们知道 AOP 的实现原理之一就是 JDK 的动态代理机制,而 JDK 的动态代理本质就是利用了反射技术来实现的

JDK 的动态代理机制中,有两个重要的类和接口,一个是 InvocationHandler 接口、一个是 Proxy 类,这一个类和接口是实现我们动态代理所必须用到的

Proxy

Proxy 类来自于 java.lang.reflect 包下,它的就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance() 这个方法

Object proxy = Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
  • loader:一个 ClassLoader 类加载器对象,定义了由哪个 ClassLoader 对象来对生成的代理对象进行加载
  • interfaces:一个 Interface 对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我代理对象就能调用这组接口中的方法了
  • h:一个 InvocationHandler 对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个 InvocationHandler 对象上

通过 Proxy.newProxyInstance() 创建的代理对象是在 JVM 运行时 动态生成的一个对象 ,它并不是我们的 InvocationHandler 类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象

InvocationHandler 接口

InvocationHandler 接口也来自于 java.lang.reflect 包下。每一个动态代理类都必须要实现 InvocationHandler 这个接口,并且每个代理类的实例都关联到了一个 handler当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由 InvocationHandler 这个接口的 invoke() 方法来进行调用

Object invoke(Object proxy, Method method, Object[] args) throws Throwable
  • proxy:指代我们所代理的那个真实对象
  • method:指代的是我们所要调用真实对象的某个方法的 Method 对象
  • args:指代的是调用真实对象某个方法时接受的参数

有关 JDK 动态代理的原理 1https://blog.csdn.net/weixin_38192427/article/details/113093728
有关 JDK 动态代理的原理 2https://blog.csdn.net/Dream_Weave/article/details/84183247

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-09-24 10:24:23  更:2021-09-24 10:25:00 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 18:59:18-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码