Java动态代理、反射

news2024/12/28 18:46:20

文章目录

  • 动态代理
    • 调用者--->代理--->对象
    • 为什么需要代理
    • 代理的详细实现过程
    • 代码详情
  • 反射
    • 反射概念
    • 反射中常用的方法
    • 所有代码

动态代理

调用者—>代理—>对象

动态代理就是无侵入式的给代码增加新的功能,通过接口保证后面的对象和代理需要实现同一个接口,接口中就是被代理的所有方法,代理里面就是对象要被代理的方法。

为什么需要代理

因为一个对象觉得自己身上的功能太多,就会将一部分功能代理出去,对象中什么方法想要被代理,在代理中必须有对应的方法,一般会定义一个接口,接口中的方法是想要被代理的方法

代理的详细实现过程

首先是有一个需要被代理的对象,然后这个对象中有哪个方法想要被代理,创建一个接口,接口中的方法就是这个对象想要被代理的方法,在这个对象中需要实现这个接口,然后重写这几个方法。这时候就需要一个代理类,这个代理类,这个代理类主要是用到里面的代理方法,这个方法的作用是给一个对象创建一个代理,将需要被代理的对象传入其中,然后返回的是代理的对象。在这个方法里面,需要调用一个方法,就是java.util.reflect.Proxy类里面的一个方法,由于这个方法是静态方法,所以可以直接使用类名进行调用,public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)在这个方法中,第一个参数是指定哪一个类加载器去加载生成的代理类,第二个参数是指定的接口,第三个参数是用于指定生成的代理对象要干什么,一般创建代理对象不会调用到第三个参数,后面只有通过代理对象进行方法回调的时候才会调用第三个参数,在执行这个第三个参数时,会重写invoke方法进行代理方法的实现,传入invoke方法有三个参数,第一个参数是传入代理的对象,第二个参数是传入需要运行的就是原本对象的方法,第三个参数就是传入这个方法的参数。在返回的时候,返回的是传入这个方法的invoke方法传回的值。

代码详情

  • 首先是定义一个方法,里面是所有的功能,这里定义了一个明星类
public class BigStar implements Star {
    private String name;

    public BigStar() {

    }

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

    public String getName() {
        return name;
    }

    public void setName(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 + "跳舞");
    }
}


  • 其次是一个需要代理的方法
/**
 * 我们可以将所有想要被代理的方法定义在接口当中
 */
public interface Star {

    // 唱歌
    String sing(String name);

    // 跳舞
    void dance();
}


  • 然后是一个代理类
public class ProxyUtil {
    /**
     * 这个方法的作用:给明星的一个对象创建一个代理
     * @param bigStar 被代理的明星对象
     * @return 给明星创建的代理
     */
    public static Star createProxy(BigStar bigStar){

        /**
         * java.util.reflect.Proxy类:提供了为对象产生代理对象的方法
         * public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
         * 参数一:用于指定用哪个类加载器去加载生成的代理类
         * 参数二:指定接口,就是这个接口用于指定生成的代理有哪些方法
         * 参数三:用于指定生成的代理对象要干什么
         */
        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 {
                        /**
                         * 参数一:代理的对象
                         * 参数二:要运行的方法
                         * 参数三:调用方法时传递的实参
                         */
                        if("sing".equals(method.getName())){
                            System.out.println("给话筒");
                        } else if ("dance".equals(method.getName())) {
                            System.out.println("准备场地");
                        }
                        return method.invoke(bigStar, args);
                    }
                }
        );

        return star;
    }
}


  • 测试代理
public class Test {
    public static void main(String[] args) {

        BigStar bigStar = new BigStar("坤哥");
        Star prosy = ProxyUtil.createProxy(bigStar);
        String res = prosy.sing("鸡你太美");
        System.out.println(res);
//        ProxyUtil.createProxy(bigStar).dance();
    }
}

反射

反射概念

反射就是可以在运行时分析类和执行类的能力,通过反射可以获得任意一个类的属性和方法,还可以调用这些类的属性和方法。比如下面这张图片。

在这里插入图片描述

反射中常用的方法

在反射中,我们可以使用三种方式调用类的字节码文件,分别是Class.forName(全类名),类名.class,对象.getClass();在这三种调用类的字节码的过程中,第一种一般市用在类加载过程中的,第二种经常作为参数进行调用,第三种一般是创建对象之后,然后使用对象名进行调用。当我们获取到类的字节码文件之后,就可以通过类的字节码文件去调用类中的构造方法,成员变量和成员方法以及他们相应的信息。构造方法(Constructor),使用getConstructors可以获取到所有的公共的构造方法,使用getDeclaredConstructors可以获取所有的构造方法,使用不加s的可以用来获取单个的构造方法,但是需要传入构造方法中传入的参数类型,比如Stirng就需要传入String.class,int就需要传入int.classDeclared是用来控制是否查找公共方法,当我们获取到某个对象之后,可以使用getModifiers获取修饰这个对象的权限修饰符(但是一般获取到的都是数字比如下图),使用getParameters获取这个对象中所有传入参数的类型,然后使用newInstance去构造这个对象,但是一般private修饰的方法会报错,需要使用setAccessible(true)消除掉当前方法的修饰权限。

在这里插入图片描述

在这里插入图片描述

反射虽然给与了我们在运行时分析类和操作类的能力,但是也增加了安全性问题,可以通过setAccessible(true)方法设置对象可被强制访问,可以破坏单例的封装性

所有代码

  • 获取类字节码文件
/**
 * Java反射
 * 反射允许对类的成员变量,成员方法,构造方法的信息进行编程访问
 * 可以获取到这三者的权限修饰符,名字,返回值类型。可以对获取到的属性赋值,或者获取已经有的值。
 * 可以获取到成员方法返回的值,还可以获取到方法抛出的异常,方法上的注解
 * 可以运行获取出来的方法
 *
 * 首先要获取.class字节码文件
 * 获取class对象的三种方式:
 * 1. Class.forName("全类名");  <源代码阶段使用>
 * 2. 类名.class   <类加载阶段使用>
 * 3. 对象.getClass();    <运行阶段使用>
 */
public class ReflectDemo1 {
    public static void main(String[] args) throws ClassNotFoundException {
        Student student = new Student();

        // 最常见的一种
        System.out.println(Class.forName("oop.reflectdemo.reflect.Student"));

        // 一般当作参数进行传递
        System.out.println(Student.class);

        // 当只有这个类的对象才可以使用
        System.out.println(student.getClass());
    }
}

  • 获取类中的构造方法,属性,以及成员方法信息
/**
 * Java反射
 * 常见的获取构造方法,成员变量,成员方法的方法
 * Java中Constructor描述构造方法,Field描述成员变量,Method描述成员方法
 */
public class ReflectDemo2 {
    public static void main(String[] args) throws Exception {

        // 获取字节码文件对象
        Class clazz = Class.forName("oop.reflectdemo.reflect.Student");

        // 获取所有公共的的构造方法
        Constructor[] constructor = clazz.getConstructors();
        for (Constructor constructor1 : constructor) {
            System.out.println(constructor1);
        }

        // 获取所有的构造方法
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
        for (Constructor declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }

        /**
         * 获取单个构造方法
         * 后面括号里面需输入这个构造方法的传入值类型
         * 一般getConstructor只能获取到public修饰的方法,而要获取到所有权限修饰的方法必须用getDeclaredConstructor;
         *
         */
        clazz.getConstructor(); // 获取无参构造

        clazz.getConstructor(String.class); // 获取传入参数为String类型的构造方法

        Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class, int.class);// 获取传入参数为int的构造方法

        // 获取修饰这个对象的权限修饰符
        int modifiers = declaredConstructor.getModifiers();
        System.out.println(modifiers);

        // 获取这个对象中所有传入参数的类型
        declaredConstructor.getParameters();

        declaredConstructor.setAccessible(true);
        Student tom = (Student) declaredConstructor.newInstance("Tom", 21);
        System.out.println(tom);


        /**
         * 操作成员变量
         */
        // 获取public修饰的所有的成员变量
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        // 获取所有成员变量
        Field[] declaredField = clazz.getDeclaredFields();
        for (Field field : declaredField) {
            System.out.println(field);
        }


        // 返回单个的成员变量对象
        Field name = clazz.getDeclaredField("name");
        System.out.println(name);
        // 获取权限修饰符
        int modifiers1 = name.getModifiers();
        System.out.println(modifiers1);

        // 获取名字
        String name1 = name.getName();
        System.out.println(name1);

        // 获取类型
        Class<?> type = name.getType();
        System.out.println(type);

        // 获取成员变量记录的值
        Student s = new Student("Tom", 32);
        name.setAccessible(true);
        Object value = name.get(s);
        System.out.println(value);

        // 修改对象里面的值
        name.set(s,"Jerry");
        System.out.println(s.getName());


        /**
         * 操作成员方法
         */
        // 获取所有的公共方法
        Method[] methods = clazz.getMethods();

        // 获取所有的方法
        Method[] declaredMethods = clazz.getDeclaredMethods();

        /**
         * 获取方法进行调用
         */
        // 获取单个方法, 传入方法名,后面跟着方法传入的参数类型
        Method eat = clazz.getMethod("eat", String.class);

        // 获取方法的权限修饰符
        int modifiers2 = eat.getModifiers();

        // 获取方法的形参
        Parameter[] parameters = eat.getParameters();

        // 获取方法抛出的异常
        Class<?>[] exceptionTypes = eat.getExceptionTypes();

        // 方法运行
        Student student = new Student();
        eat.setAccessible(true);
        eat.invoke(student,"汉堡");


    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kubernetes教程—查看 Pod 和节点

目标 了解 Kubernetes Pod。了解 Kubernetes 节点。对已部署的应用故障排除。 Kubernetes Pod 在模块 2 中创建 Deployment 时, Kubernetes 创建了一个 Pod 来托管你的应用实例。Pod 是 Kubernetes 抽象出来的&#xff0c; 表示一组一个或多个应用容器&#xff08;如 Docker…

Nexus2迁移升级到Nexus3

与 Nexus 2.x 相比&#xff0c;Nexus 3.x 为我们提供了更多实用的新特性。SonaType 官方建议我们&#xff0c;使用最新版本 Nexus 2.x 升级到最新版本 Nexus 3.x&#xff0c;并在 Nexus 升级兼容性 一文中为我们提供了各个版本 Nexus 升级到最新版本 Nexus 3.x 的流程&#xff…

opencv如何调用YOLOv5(无pytorch)

目录 一、前言 二.正文 2.1定义颜色 2.2目标检测主代码详解 2.3读取视频or图片进行检测 注意&#xff1a;opencv-python 本文使用的版本为4.5.2.52 一、前言 YOLO系列是one-stage且是基于深度学习的回归方法&#xff0c;而R-CNN、Fast-RCNN、Faster-RCNN等是two-stage且…

情人节特别定制:多种语言编写动态爱心网页(附完整代码)

写在前面案例1&#xff1a;HTML Three.js库案例2&#xff1a;HTML CSS JavaScript案例3&#xff1a;Python环境 Flask框架结语 写在前面 随着七夕节的临近&#xff0c;许多人都在寻找独特而令人难忘的方式来表达爱意。在这个数字时代&#xff0c;结合创意和技术&#xff0…

maven 从官网下载指定版本

1. 进入官网下载页面 Maven – Download Apache Maven 点击下图所示链接 2. 进入文件页&#xff0c;选择需要的版本 3. 选binaries 4. 选文件&#xff0c;下载即可

十亿次实验,用概率解读周易大衍筮法的奥秘

还记得封神电影里的文王占卜吗&#xff1f; 也就是著名的大衍筮法。 《易传》曰&#xff1a;大衍之数五十&#xff0c;其用四十有九。分而为二以象两&#xff0c;挂一以象三&#xff0c; 揲之以四以象四时&#xff0c;归奇于扐以象闰&#xff0c;五岁再闰&#xff0c;故再扐而…

苹果电脑怎么录屏?步骤详解,看到就是赚到

苹果电脑作为一款受欢迎的高性能设备&#xff0c;不仅在日常工作中发挥着重要作用&#xff0c;还可以用于创造内容&#xff0c;如录制屏幕内容。录屏功能能够帮助用户将屏幕上的活动记录成视频&#xff0c;方便分享、演示或存档。可是您知道苹果电脑怎么录屏吗&#xff1f;通过…

Lnton羚通云算力平台【PyTorch】教程:torch.nn.SiLU

torch.nn.SiLU 原型 CLASS torch.nn.SiLU(inplaceFalse) torch.nn.SiLU 是 PyTorch 深度学习框架中的一个激活函数&#xff0c;它代表 Sigmoid-Weighted Linear Unit&#xff08;SiLU&#xff09;&#xff0c;也称为 Swish 激活函数。SiLU 激活函数在深度学习中被广泛使用&…

Unittest+Selenium模块驱动自动化测试实战

UnittestSelenium自动化测试框架使用模块驱动测试模型将冗余的代码封装成类&#xff0c;且基于PageObject的自动化设计模式&#xff0c;通过分层的方式将页面对象、操作、业务分开处理。 1、首先创建自动化测试框架的文件模块架构&#xff0c;创建common、base、testcase、rep…

Kafka单节点部署

&#x1f388; 作者&#xff1a;互联网-小啊宇 &#x1f388; 简介&#xff1a; CSDN 运维领域创作者、阿里云专家博主。目前从事 Kubernetes运维相关工作&#xff0c;擅长Linux系统运维、开源监控软件维护、Kubernetes容器技术、CI/CD持续集成、自动化运维、开源软件部署维护…

1.0的星火2.0必将燎原——图文声影PPT全测试

一、前言 大家好&#xff0c;勇哥又来分享AI模型了&#xff0c;前几天讯飞发布的星火大模型2.0迅速的进入了我们圈子里&#xff0c;为了有更多更好的模型分享给大家&#xff0c;分享星火大模型2.0是必须做的&#xff0c;我做一个传递着&#xff0c;希望大家也星火相传啊。 我…

iPhone备忘录删除了怎么恢复?3个妙招教你快速复原

【为了清理手机内存&#xff0c;一键清空了备忘录&#xff0c;突然想起有很多重要的笔记还存在里面&#xff0c;有什么办法能还原回来吗&#xff1f;】 备忘录是一个非常实用的工具&#xff0c;能够帮助大家记录各种各样的信息&#xff0c;并提醒大家按时完成任务&#xff0c;…

19.Helm

文章目录 Helm简介三个概念版本部署HelmHelm命令Helm 自定义模板基于原有的软件包进行修改自建软件包软件包升级软件包升级和创建ingress回滚版本 仓库关联部署harbor安装 push 插件 总结 Helm 简介 Helm本质就是让K8s的应用管理&#xff08;Deployment、Service等&#xff09…

前端打开弹窗时将链接转化为二维码

qrcodejs2 1.安装qrcodejs2 2.在使用页面中引入 import QRCode from "qrcodejs2";3.在组件中注册(Vue2项目) components: {QRCode,}, 4.在data中定义qrcode&#xff0c;以及方法中使用 showCode(row) {this.dialogVisible true;this.$nextTick(() > { 需要n…

Docker+Jmeter+InfluxDB+Grafana 搭建性能监控平台

当今互联网发展迅速&#xff0c;应用程序的性能监控显得越来越重要。 DockerJmeterInfluxDBGrafana 是一种常用的性能监控平台&#xff0c;可以帮助开发者快速搭建一套可靠的监控体系。在本文中&#xff0c;我们将介绍如何使用这些工具搭建性能监控平台&#xff0c;以便开发人…

R语言处理缺失数据(1)-mice

#清空 rm(listls()) gc()###生成模拟数据### #生成100个随机数 library(magrittr) set.seed(1) asd<-rnorm(100, mean 60, sd 10) %>% round #平均60&#xff0c;标准差10 #将10个数随机替换为NA NA_positions <- sample(1:100, 10) asd[NA_positions] <- NA #转…

哈夫曼树介绍及Java实现

哈夫曼树 1. 介绍1.1 哈夫曼树1.2 路径、路径长度、结点的权、结点的带权路径长度1.3 树的带权路径长度WPL 2. 哈夫曼树构建步骤3. 代码实现 1. 介绍 1.1 哈夫曼树 哈夫曼树-最优二叉树&#xff1a;树的带权路径长度最小的二叉树&#xff1b;权值均为叶子结点&#xff1b;权值…

云计算与边缘计算:加速数字化转型的关键驱动力

云计算和边缘计算技术正以惊人的速度改变着企业的业务和基础架构。这些先进的技术为企业带来了灵活性、可扩展性和成本效益的优势&#xff0c;重新定义了业务运作的方式。 云计算是通过互联网将计算资源提供给用户的一种服务模式。通过云计算&#xff0c;企业可以将应用程序、…

Vulkan基础

目录 一、Vulkan开发理论基础知识 接口设计理念 Host&Device 基础设施——元数据和设备 基础设施——交换链 ​编辑交换链 SwapChain​编辑 渲染管线 Pipeline RenderPass CommandBuffer 二、Vulkan DescriptorSet 创建DescriptorPool 运行时绑定DescriptorSet 三…

Egg.js + Joi 进行接口参数验证

Joi 是一款强大的 JavaScript 数据验证库&#xff0c;用于验证和转换数据的格式。无论是在后端还是前端开发中&#xff0c;数据验证都是确保数据完整性和一致性的关键步骤。Joi 提供了一种简洁而灵活的方式来定义验证规则&#xff0c;以确保输入数据满足预期要求。 本文将介绍如…