【JavaSE】Java中的反射动态代理

本篇文章整理的内容来源于: 反射原理

文章目录

  • 一. 动态代理
    • 1. 优点
    • 2. 动态代理三要素
    • 3. 创建代理对象并使用
  • 二. 反射
    • 1. 什么是反射
    • 2. 获取字节码文件对象的三种方式
      • (1) Class.forName()获取 (源代码阶段)
      • (2) 通过class属性获取
      • (3) 通过对象获取字节码文件对象
    • 3. 获取构造方法
    • 4. 获取构造方法并创建对象
      • (1)获取空参,并创建对象.
      • (2)获取带参构造,并创建对象
    • 5. 获取成员变量
    • 6. 获取成员方法
    • 7. 反射的作用

一. 动态代理

1. 优点

使用动态代理能够无侵入式的给方法增强功能.

所谓无侵入式,指的是在原有代码的基础上,不需要修改原始类的源代码,就可以通过代理来增强其功能。通过动态代理,我们可以在代理对象的方法执行前后插入额外的逻辑,例如日志记录、性能监测、事务管理等。这样可以在不修改原始类的情况下,通过代理对象对方法进行增强,达到扩展功能的目的。

动态代理利用了反射机制,在运行时动态地生成代理类和代理对象,从而实现对原始对象的包装。通过调用代理对象的方法,实际上是调用了被代理对象的方法,并在方法执行前后执行额外的逻辑。

2. 动态代理三要素

  1. 真正执行任务的对象.
  2. 代理对象
  3. 利用代理调用方法

在这里插入图片描述

3. 创建代理对象并使用

如何为Java对象创建一个代理对象呢?

我们可以使用java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

参数一:用于指定用哪个类加载器,去加载生成的代理类
参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
参数三:用来指定生成的代理对象要干什么事情

package com.xxxflower.demo1;
public class BigStar implements Star {
    private String name;


    public BigStar() {
    }

    public BigStar(String name) {
        this.name = name;
    }

    //唱歌
    @Override
    public String sing(String name){
        System.out.println(this.name + "正在唱" + name);
        return "谢谢";
    }

    //跳舞
    @Override
    public void dance(){
        System.out.println(this.name + "正在跳舞");
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    public String toString() {
        return "BigStar{name = " + name + "}";
    }
}

package com.xxxflower.demo1;
public interface Star {
    //我们可以把所有想要被代理的方法定义在接口当中
    //唱歌
    public abstract String sing(String name);
    //跳舞
    public abstract void dance();
}

package com.xxxflower.demo1;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/*
 *
 * 类的作用:
 *       创建一个代理
 *
 * */
public class ProxyUtil {
    /*
     *
     * 方法的作用:
     *       给一个明星的对象,创建一个代理
     *
     *  形参:
     *       被代理的明星对象
     *
     *  返回值:
     *       给明星创建的代理
     *
     *
     *
     * 需求:
     *   外面的人想要大明星唱一首歌
     *   1. 获取代理的对象
     *      代理对象 = ProxyUtil.createProxy(大明星的对象);
     *   2. 再调用代理的唱歌方法
     *      代理对象.唱歌的方法("只因你太美");
     * */
    public static Star createProxy(BigStar bigStar){
        Star star = (Star) Proxy.newProxyInstance(
                ProxyUtil.class.getClassLoader(),//参数一:用于指定用哪个类加载器,去加载生成的代理类
                new Class[]{Star.class},//参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
                //参数三:用来指定生成的代理对象要干什么事情
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        /*
                         * 参数一:代理的对象
                         * 参数二:要运行的方法 sing
                         * 参数三:调用sing方法时,传递的实参
                         * */
                        if("sing".equals(method.getName())){
                            System.out.println("准备话筒,收钱");
                        }else if("dance".equals(method.getName())){
                            System.out.println("准备场地,收钱");
                        }
                        //去找大明星开始唱歌或者跳舞
                        //代码的表现形式:调用大明星里面唱歌或者跳舞的方法
                        return method.invoke(bigStar,args);
                    }
                }
        );
        return star;
    }
}
package com.xxxflower.demo1;

public class Test {
    public static void main(String[] args) {
    /*
        需求:
            外面的人想要大明星唱一首歌
             1. 获取代理的对象
                代理对象 = ProxyUtil.createProxy(大明星的对象);
             2. 再调用代理的唱歌方法
                代理对象.唱歌的方法("只因你太美");
     */
        //1. 获取代理的对象
        BigStar bigStar = new BigStar("鸡哥");
        Star proxy = ProxyUtil.createProxy(bigStar);

        //2. 调用唱歌的方法
        String result = proxy.sing("只因你太美");
        System.out.println(result);
    }
}

在这里插入图片描述
程序执行结果:
在这里插入图片描述

二. 反射

1. 什么是反射

Java中的反射(Reflection),是指在运行时动态地获取类的信息,以及动态地调用对象的方法和属性。简而言之,就是程序在运行时可以动态地获取类的信息并对其进行操作

Java反射机制允许程序在运行时检查和操作类、接口、字段、方法以及构造方法等元素,例如:

  1. 获取类的名称、修饰符、父类、实现的接口等信息。
  2. 动态地创建对象,即使不知道具体类的名称。
  3. 获取或设置字段的值,即使它们是私有的。
  4. 调用对象的方法,即使不知道方法的名称或参数列表。
  5. 获取或设置方法的注解信息等。

反射机制为Java编程提供了更大的灵活性和扩展性,尤其在框架设计、动态代理、JavaBean操作等方面得到广泛应用。但是由于反射会牺牲一定的性能,因此在性能要求较高的场景下,应该谨慎使用反射。

2. 获取字节码文件对象的三种方式

反射都是从class字节码文件中获取的内容。因此如果我们想使用反射,就必须首先获取class字节码文件的对象.
获取字节码文件对象的三种方式如下

(1) Class.forName()获取 (源代码阶段)

我们可以通过Class这个类里面的静态方法forName(“全类名”)来获取.示例:
注意: 全类名 = 包名 + 类名

Class clazz1 = Class.forName("com.xxxflower.reflectdemo.Student");

源代码阶段获取 — > 先把Student加载到内存中,再获取字节码文件的对象
clazz 就表示Student这个类的字节码文件对象。即Student.class这个文件加载到内存之后,产生的字节码文件对象.

(2) 通过class属性获取

通过 类名.class 示例如下:

Class clazz2 = Student.class;

class文件在硬盘中是唯一的.
所以,当这个文件加载到内存之后产生的对象也是唯一的.

(3) 通过对象获取字节码文件对象

示例:

Student s = new Student();
Class clazz3 = s.getClass();

此处注意几个概念:
字节码文件:通过.java文件编译之后的.class文件(真实存在的)
字节码文件对象:当.class文件加载到内存之后,虚拟机自动创建出来的对象.这个对象里面至少包含了:构造方法,成员变量,成员方法。

我们反射所获取的就是字节码文件对象.

3. 获取构造方法

方法名说明
Constructor<?>[] getConstructors()获得所有的构造(只能public修饰)
Constructor<?>[] getDeclaredConstructors()获得所有的构造(包含private修饰)
Constructor getConstructor(Class<?>… parameterTypes)获取指定构造(只能public修饰)
Constructor getDeclaredConstructor(Class<?>… parameterTypes)获取指定构造(包含private修饰)

代码示例:

public class ReflectDemo2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        //1.获得整体(class字节码文件对象)
        Class clazz = Class.forName("com.itheima.reflectdemo.Student");


        //2.获取构造方法对象
        //获取所有构造方法(public)
        Constructor[] constructors1 = clazz.getConstructors();
        for (Constructor constructor : constructors1) {
            System.out.println(constructor);
        }

        System.out.println("=======================");

        //获取所有构造(带私有的)
        Constructor[] constructors2 = clazz.getDeclaredConstructors();

        for (Constructor constructor : constructors2) {
            System.out.println(constructor);
        }
        System.out.println("=======================");

        //获取指定的空参构造
        Constructor con1 = clazz.getConstructor();
        System.out.println(con1);

        Constructor con2 = clazz.getConstructor(String.class,int.class);
        System.out.println(con2);

        System.out.println("=======================");
        //获取指定的构造(所有构造都可以获取到,包括public包括private)
        Constructor con3 = clazz.getDeclaredConstructor();
        System.out.println(con3);
        //了解 System.out.println(con3 == con1);
        //每一次获取构造方法对象的时候,都会新new一个。

        Constructor con4 = clazz.getDeclaredConstructor(String.class);
        System.out.println(con4);
    }
}

4. 获取构造方法并创建对象

(1)获取空参,并创建对象.

示例:

//1.获取整体的字节码文件对象
Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");
//2.获取空参的构造方法
Constructor con = clazz.getConstructor();
//3.利用空参构造方法创建对象
Student stu = (Student) con.newInstance();
System.out.println(stu);

(2)获取带参构造,并创建对象

示例:

//1.获取整体的字节码文件对象
Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");
//2.获取有参构造方法
Constructor con = clazz.getDeclaredConstructor(String.class, int.class);
//3.临时修改构造方法的访问权限(暴力反射)
con.setAccessible(true);
//4.直接创建对象
Student stu = (Student) con.newInstance("zhangsan", 23);
System.out.println(stu);

5. 获取成员变量

方法名说明
Field[] getFields()返回所有成员变量对象的数组(只能拿public的)
Field[] getDeclaredFields()返回所有成员变量对象的数组,存在就能拿到
Field getField(String name)返回单个成员变量对象(只能拿public的)
Field getDeclaredField(String name)返回单个成员变量对象,存在就能拿到

6. 获取成员方法

方法名说明
Method[] getMethods()返回所有成员方法对象的数组(只能拿public的)
Method[] getDeclaredMethods()返回所有成员方法对象的数组,存在就能拿到
Method getMethod(String name, Class<?>… parameterTypes)返回单个成员方法对象(只能拿public的)
Method getDeclaredMethod(String name, Class<?>… parameterTypes)返回单个成员方法对象,存在就能拿到

7. 反射的作用

  1. 获取任意一个类中的所有信息
  2. 结合配置文件动态创建对象

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/303259.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于深度学习的停车位关键点检测系统(代码+原理)

摘要&#xff1a; DMPR-PS是一种基于深度学习的停车位检测系统&#xff0c;旨在实时监测和识别停车场中的停车位。该系统利用图像处理和分析技术&#xff0c;通过摄像头获取停车场的实时图像&#xff0c;并自动检测停车位的位置和状态。本文详细介绍了DMPR-PS系统的算法原理、…

django websocket实现聊天室功能

注意事项channel版本 django2.x 需要匹配安装 channels 2 django3.x 需要匹配安装 channels 3 Django3.2.4 channels3.0.3 Django3.2.* channels3.0.2 Django4.2 channles3.0.5 是因为最新版channels默认不带daphne服务器 直接用命令 python manage.py runsever 默认运行的是w…

数据库系统-甘晴void学习笔记

数据库系统笔记 计科210X 甘晴void 202108010XXX 教材&#xff1a;《数据库系统概论》第6版 &#xff08;图片来源于网络&#xff0c;侵删&#xff09; 文章目录 数据库系统<br>笔记第一篇 基础篇1 绪论1.1数据库系统概述1.2数据模型1.3数据库系统的结构(三级模式结构…

JSP内置对象:forward动作标记和sendRedirect()方法实现页面跳转的区别

1.forward为服务器跳转&#xff0c;浏览器的地址栏不变&#xff1b; sendRedirect&#xff08;&#xff09;为客户端跳转&#xff0c;浏览器的地址栏改变变新页面的URL。 2.执行到forward标记出现处停止当前JSP页面的继续执行&#xff0c;而转向标记中的page属性指定的页面&am…

k8s的三种发布方式

三种常见的发布方式 应用程序升级面临最大挑战是新旧业务切换&#xff0c;将软件从测试的最后阶段带到生产环境&#xff0c;同时要保证系统不间断提供服务。而最为常见三种发布方式分别为&#xff1a;蓝绿发布&#xff0c;灰度发布和滚动发布。 三种发布方式的最终目的都是为了…

12 月 NFT 市场动态:强劲增长塑造年终趋势

作者&#xff1a;stellafootprint.network 数据来源&#xff1a;NFT Research 12 月加密货币和 NFT 领域出现了显著的上涨趋势&#xff0c;比特币和以太坊价格的大幅上涨标志着市场的复苏。与此同时&#xff0c;NFT 领域的交易量飙升&#xff0c;独立用户&#xff08;钱包&am…

508基于51单片机的火灾检测与报警系统设计

基于51单片机的火灾检测与报警系统设计[proteus仿真] 火灾检测与报警系统这个题目算是课程设计和毕业设计中常见的题目了&#xff0c;本期是一个基于51单片机的火灾检测与报警系统设计 需要的源文件和程序的小伙伴可以关注公众号【阿目分享嵌入式】&#xff0c;赞赏任意文章 …

【开源硬件篇】STM32F103C8T6核心板

STM32F103C8T6核心板 文章目录 STM32F103C8T6核心板一、STM32F103C8T6芯片1.1 STM32F103C8T6简介1.2 芯片引脚说明 二、去耦电路2.1 原理图设计2.2 原理分析2.2.1 结论2.2.2 去耦效果图2.2.3 放置距离问题2.2.3 放置位置问题 2.3 PCB设计示例 三、晶振电路3.1 原理图设计3.2 原…

龙芯+RT-Thread+LVGL实战笔记(29)——电子琴弹奏

【写在前面】临近期末,笔者工作繁忙,因此本系列教程的更新频率有所放缓,还望订阅本专栏的朋友理解,请勿催更。笔者在此也简要声明几点: 有些硬件模块笔者并没有,如LED点阵、压力传感模块、RFID模块等,因此这些模块的相关任务暂时无法给出经过验证的代码。其实,教程进行…

uniapp地图兼容小程序和APP(高德地图),点击marker并弹框详情

1.uniapp地图页面兼容小程序和APP 2.小程序使用map组件APP使用高德地图 3.点击定位按钮地图自动移动到定位点 4.APP地图逻辑是视图层交互使用renderjs 5.点击地图marker弹框详情 6.全部代码 <template><page-meta :page-style"overflow:(show?hidden:visible)&…

视频号小店怎么上架商品?实操分享,干货满满!

我是电商珠珠 视频号小店从22年7月到现在也不过才发展了一年&#xff0c;它的风口才刚刚开始。 平台为了吸引商家入驻&#xff0c;会将大量红利向商家倾斜&#xff0c;只要把握住风口&#xff0c;就会很快起飞。 视频号小店对于很多人来说&#xff0c;都是新平台&#xff0c…

P9 视频码率及其码率控制方式

前言 从本章开始我们将要学习嵌入式音视频的学习了 &#xff0c;使用的瑞芯微的开发板 &#x1f3ac; 个人主页&#xff1a;ChenPi &#x1f43b;推荐专栏1: 《C_ChenPi的博客-CSDN博客》✨✨✨ &#x1f525; 推荐专栏2: 《Linux C应用编程&#xff08;概念类&#xff09;_C…

技术学习周刊第 1 期

2018 年参与过 1 年的 ARTS 打卡&#xff0c;也因为打卡有幸加入了 MegaEase 能与皓哥&#xff08;左耳朵耗子&#xff09;共事。时过境迁&#xff0c;皓哥已经不在了&#xff0c;自己的学习梳理习惯也荒废了一段时间。 2024 年没给自己定具体的目标&#xff0c;只要求自己好好…

Tomcat 的 work 目录缓存导致的JSP页面图片更新问题

一、问题分析 1. 修改后重新部署没有变化 笔者之前部署了一个后台管理项目&#xff0c;通过它来发布课程内容&#xff0c;其中有一个 JSP 课程页面&#xff0c;在该 JSP 页面里也引用了类文件 Constant.java 里的一个变量&#xff08;ALIYUN_OSS_PATH&#xff09;&#xff0c;…

YOLOv5改进之---EIoU,SIoU,AlphaIoU,FocalEIoU,Wise-IoU

本文主要针对yolov5的iou损失函数进行改进,主要涵盖EIoU,SIoU,AlphaIoU,FocalEIoU,Wise-IoU这几种损失。 一、首先会对这几种损失进行介绍: 1、背景 由于EIOU是在CIOU的基础上改进的,为方便理解,此处贴出CIOU的计算公式,其他GIOU、DIOU不作介绍。 CIOU公式如下所示: b ,…

(二)Explain使用与详解

explain中的列 sql语句: EXPLAIN SELECT * from user WHERE userId=1340; 执行结果: 1. id列 id列的编号是 select 的序列号,有几个 select 就有几个id,并且id的顺序是按 select 出现的顺序增长的。 id列越大执行优先级越高,id相同则从上往下执行,id为NULL最后执行…

基于yolov2深度学习网络的车辆行人检测算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022a 3.部分核心程序 .......................................................... load yolov2.mat% 加载训练好的…

余震强度预测能力升级,Nature 刊文认证基于神经网络的模型性能优于传统模型

作者&#xff1a;李宝珠 编辑&#xff1a;李玮栋、xixi&#xff0c;三羊 地震的发生涉及诸多变量&#xff0c;「预测」存在挑战&#xff0c;但余震发生次数及强度的预测已取得重大进展。 2023 年 12 月 18 日 23 时 59 分&#xff0c;甘肃省临夏州积石山县发生 6.2 级地震&…

华清远见作业第二十三天——IO(第六天)

使用有名管道完成两个进程之间相互通信 代码&#xff1a; 创建管道&#xff1a; #include<a.h> int main(int argc, const char *argv[]) {//创建有名管道文件if(mkfifo("./myfifo1", 0664) ! 0){perror("mkfifo1 error");return -1;}printf("…

Open3D mesh模型平滑处理 (9)

Open3D mesh模型平滑处理 &#xff08;9&#xff09; 一、模型平滑示意图二、平滑函数介绍三、具体实现1.代码 一、模型平滑示意图 生成球体并添加高斯噪声的模型 平滑后&#xff0c;参数简单设置一下 二、平滑函数介绍 在Open3D中&#xff0c;filter_smooth_taubin方法的三…