Java反射机制和动态代理

反射和动态代理

  • 反射
    • 前言
    • 获取class对象的方式
    • 反射获取构造方法
    • 反射获取成员变量
    • 反射获取成员方法
    • 实例
  • 动态代理

反射

前言

什么是反射?
反射允许对成员变量,成员方法和构造方法的信息进行编程访问。
为什么用反射 / 反射的作用?
可以轻易地获取成员变量、构造方法和成员方法的所有信息。
①获取一个类里面所有的信息,获取到了之后,再执行其他的业务逻辑。
②结合配置文件,动态的创建对象并调用方法。

获取class对象的方式

三种方式
Class.forName("全类名");
类名.class
对象.getClass()
使用时机
这三种方式的使用时机实际上和Java文件的编译过程息息相关:
在这里插入图片描述

源代码阶段没有把代码加载到内存当中,只是在硬盘中进行的操作,这一阶段使用第一种方式获得class字节码文件的对象;运行Java代码时,需要将类的字节码文件加载到内存中,这个阶段是加载阶段,该阶段使用第二种方式;在内存中创建某个类的对象时为运行阶段,该阶段使用第三种方式。

具体实现

//1. Class.forName("全类名") 全类名就是:包名+类名
//clazz_one就是Student类的字节码文件
//第一种方式最为常用
Class clazz_one = Class.forName("com.wmy.myreflect1.Student");
System.out.println(clazz_one);

//2. 类名.class
//第二种方式更多的是当做参数来传递
Class clazz_two = Student.class;
System.out.println(clazz_two);

//3. 对象.getClass()
//当我们已经有了这个类的对象时 才可以使用方式三
Class clazz_three = new Student().getClass();
System.out.println(clazz_three);
System.out.println(clazz_one == clazz_two);
System.out.println(clazz_two == clazz_three);
System.out.println(clazz_one == clazz_three);

运行结果
在这里插入图片描述

反射获取构造方法

在Java中,存在“万物皆对象”的思想,比如说class字节码文件是Class类的对象,那么构造方法可以看作是Constructor类的对象、成员变量(字段)可以看作是Field类的对象、成员方法可以看作是Method类的对象。

//1. 获取class字节码文件对象
Class clazz = Class.forName("com.wmy.myreflect1.demo02.Student02");
//2. 获取构造方法
System.out.println("获取公共构造方法:");
Constructor[] cons1 = clazz.getConstructors();
for (Constructor con : cons1){
	System.out.println(con);
}
System.out.println("分割线-----------------------------------"+"\n");
System.out.println("获取所有构造方法(含私有):");
Constructor[] cons2 = clazz.getDeclaredConstructors();
for (Constructor con : cons2){
	System.out.println(con);
}
System.out.println("注:虽然该方法可以获得私有构造方法,但是无法通过这种方式完成私有构造方法new一个对象出来");
System.out.println("分割线-----------------------------------"+"\n");
System.out.println("根据构造方法中参数类型和参数数量的不同 向getDeclaredConstructor中传入参数类型的字节码文件即可获得对应的构造方法:");
Constructor con1 = clazz.getDeclaredConstructor();
Constructor con2 = clazz.getDeclaredConstructor(String.class);
Constructor con3 = clazz.getDeclaredConstructor(int.class);
Constructor con4 = clazz.getDeclaredConstructor(String.class, int.class);
System.out.println(con1);
System.out.println(con2);
System.out.println(con3);
System.out.println(con4);
System.out.println("分割线-----------------------------------"+"\n");
System.out.println("获取权限修饰符");
int modifiers = con4.getModifiers();
System.out.println("1表示公有public 2表示私有private");
System.out.println(modifiers);
System.out.println("分割线-----------------------------------"+"\n");
System.out.println("暴力反射:表示临时取消权限校验");
con4.setAccessible(true);
Student02 stu = (Student02)con4.newInstance("张三", 23);
System.out.println(stu);

在这里插入图片描述

反射获取成员变量

//1. 获取class字节码文件的对象
Class clazz = Class.forName("com.wmy.myreflect1.demo03.Student03");
//2. 获取成员变量
System.out.println("获取所有公共成员变量:");
Field[] fields1 = clazz.getFields();
for (Field field : fields1){
 	System.out.println(field);
}
System.out.println("分割线-----------------"+"\n");
System.out.println("获取所有成员变量(含私有):");
Field[] fields2 = clazz.getDeclaredFields();
for (Field field : fields2){
	System.out.println(field);
}
System.out.println("分割线-----------------"+"\n");
System.out.println("获取单个成员变量:");
Field gender = clazz.getField("gender");
System.out.println(gender);
Field name = clazz.getDeclaredField("name");
System.out.println(name);
Field age = clazz.getDeclaredField("age");
System.out.println(age);
System.out.println("分割线-----------------"+"\n");
System.out.println("获取权限修饰符:");
int modifiers = name.getModifiers();
System.out.println(modifiers);
System.out.println("获取成员变量名称:");
String n = name.getName();
System.out.println(n);
System.out.println("获取成员变量类型:");
Class t = name.getType();
System.out.println(t);
System.out.println("获取成员变量记录的值:");
Student03 s = new Student03("张三",23,"男");
name.setAccessible(true);
String value = (String) name.get(s);
System.out.println(value);
System.out.println("修改对象里面记录的值:");
name.set(s,"李四");
System.out.println(s);

在这里插入图片描述

反射获取成员方法

//1. 获取class字节码文件对象
Class clazz = Class.forName("com.wmy.myreflect1.demo04.Student04");
//2. 获取成员方法
System.out.println("获取所有方法对象(包含父类中所有的公共方法):");
Method[] methods1 = clazz.getMethods();
for (Method method : methods1){
    System.out.println(method);
}
System.out.println("分割线-----------------"+"\n");
System.out.println("获取所有方法对象(不能获取父类的,但是可以获取本类中私有的方法):");
Method[] methods2 = clazz.getDeclaredMethods();
for (Method method : methods2){
    System.out.println(method);
}
System.out.println("分割线-----------------"+"\n");
System.out.println("获取指定的单一方法:");
Method m = clazz.getDeclaredMethod("eat", String.class, int.class);
System.out.println(m);
System.out.println("获取方法的修饰符:");
int modifiers = m.getModifiers();
System.out.println(modifiers);
System.out.println("获取方法的名字:");
String name = m.getName();
System.out.println(name);
System.out.println("获取方法的形参:");
Parameter[] parameters = m.getParameters();
for (Parameter parameter : parameters) {
    System.out.println(parameter);
}
System.out.println("获取方法抛出的异常:");
Class[] exceptionTypes = m.getExceptionTypes();
for (Class exceptionType : exceptionTypes) {
    System.out.println(exceptionType);
}
System.out.println("获取方法的返回值:");
Student04 s = new Student04();
m.setAccessible(true);
String result = (String)m.invoke(s,"石乐志",996);
System.out.println(result);

实例

需求1
保存信息:对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中去。
实现

public class MyReflectDemo {
    public static void main(String[] args) throws IllegalAccessException, IOException {
        /*
        * 对于任意一个对象 都可以把对象所有的字段名和值 保存到文件中去
        * */
        Student s = new Student("小A" , 23, '女' , 167.5 , "睡觉");
        Teacher t = new Teacher("gls" , 1000);

        saveObject(t);
    }
    //把对象的所有成员变量名和值保存到本地文件中
    private static void saveObject(Object obj) throws IllegalAccessException, IOException {
        //1. 获取字节码文件的对象
        Class clazz = obj.getClass();
        //创建IO流
        BufferedWriter bw = new BufferedWriter(new FileWriter("G:\\JavaWorks_IntelliJIDEA\\myreflect\\src\\main\\java\\com\\wmy\\myreflect1\\record.txt"));

        //2. 获取所有的成员变量
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            //获取成员变量的名字
            String name = field.getName();
            //获取成员变量的值
            Object value = field.get(obj);
            //写出数据
            bw.write(name+"="+value);
            bw.newLine();
        }
        bw.close();
    }
}

结果
在这里插入图片描述
需求2
跟配置文件结合动态创建:反射可以和配置文件结合的方式,动态的创建对象,并调用方法。
实现
prop.properties

classname=com.wmy.myreflect1.case2.Student
method=study

com/wmy/myreflect1/case2/MyReflectDemo.java

public class MyReflectDemo {
    public static void main(String[] args) throws IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
        /*
        * 反射可以和配置文件结合的方式 动态地创建对象 并调用方法
        * */
        //1.读取配置文件中的信息
        Properties prop = new Properties();
        FileInputStream fis = new FileInputStream("G:\\JavaWorks_IntelliJIDEA\\myreflect\\prop.properties");
        prop.load(fis);
        fis.close();
        System.out.println(prop);

        //2.获取全类名和方法名
        String className = (String)prop.get("classname");
        String methodName = (String)prop.get("method");
        System.out.println(className);
        System.out.println(methodName);

        //3.利用反射去创建对象并运行方法
        Class clazz = Class.forName(className);

        //获取构造方法
        Constructor con = clazz.getDeclaredConstructor();
        Object o = con.newInstance();
        System.out.println(o);

        //获取成员方法并运行
        Method method = clazz.getDeclaredMethod(methodName);
        method.setAccessible(true);
        method.invoke(o);
    }
}

动态代理

1.什么是动态代理?
动态代理可以无侵入式的给代码增加额外的功能。
在这里插入图片描述
2.程序为什么需要代理?
对象如果嫌身上干的事太多的话,可以通过代理来转移部分职责。
对象有什么方法想被代理,代理就一定要有对应的方法。
3.代理长什么样?
代理里面就是对象要被代理的方法。
4.Java通过什么来保证代理的样子?
通过接口保证,后面的对象和代理需要实现同一个接口,接口中就是被代理的所有方法。
5.如何为Java对象创建一个代理对象?
java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interface,InvocationHandler h)
参数一:用于指定用哪个类加载器 去加载生成的代理类
参数二:指定接口 这些接口用于指定生成的代理长什么样 也就是有哪些方法
参数三:用来指定生成的代理对象要干什么事情

需求
实现BigStar类中成员方法的动态代理。
步骤
1.创建BigStar类(记得实现步骤2中的接口)
com/wmy/mydynamicproxy/BigStar.java

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+"正在让你蠢蠢欲动");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "BigStar{" +
                "name='" + name + '\'' +
                '}';
    }
}

2.封装要代理的方法为接口
com/wmy/mydynamicproxy/Star.java

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

3.创建ProxyUtil类用于创建代理
com/wmy/mydynamicproxy/ProxyUtil.java

/*
* 类的作用:
*       创建一个代理
* */
public class ProxyUtil {
    /*
    * 方法的作用:
    *       给一个明星的对象创建一个代理
    * 形参:
    *       被代理的明星对象
    * 返回值:
    *       给明星创建的代理
    * 需求:
    *       外面的人想要大明星唱一首歌
    *       1.获取代理的对象
    *           代理对象 = ProxyUtil.createProxy(大明星的对象);
    *       2.再调用代理的唱歌的方法
    *           代理对象.唱歌的方法();
    * */
    public static Star createProxy(BigStar bigStar){
        /*
        * public static Object newProxyInstance(ClassLoader loader, Class<?>[] interface,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 {
                        /*
                         * 参数一:代理的对象
                         * 参数二:要运行的方法 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;
    }
}

4.创建测试类测试动态代理的过程
com/wmy/mydynamicproxy/Test.java

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

5.效果
在这里插入图片描述

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

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

相关文章

Scene Creator

场景创建器是一个方便、易于使用的编辑工具&#xff0c;旨在简化创建新场景的过程。使用场景创建器&#xff0c;您可以选择一个模板场景&#xff0c;定义一个目录来存储您的场景&#xff0c;并在需要时自动将新场景添加到构建中。 下载&#xff1a; ​​Unity资源商店链接 资…

微软好听的tts语音包下载,粤语,韩语,日语

微软的 tts 语音库&#xff0c;都是离线的&#xff0c;所以速度非常快 但资源比较少&#xff0c;比如粤语&#xff0c;韩语&#xff0c;日语 我发现一个老牌语音技术供应商。 资源丰富&#xff0c;可自行下载免费或收费语音包。 网站&#xff1a;正版用户专用配套播音员下载…

im6ull学习总结(三-2)文字显示中文字符

承接上篇文章 中文字符的点阵显示 使用点阵字库时&#xff0c;中文字符的显示原理跟 ASCII 字符是一样的。要注意的地方在于中文的编码&#xff1a;在 C 源文件中它的编码方式是 GB2312 还是 UTF-8&#xff1f;编译出的可执行程序&#xff0c;其中的汉字编码方式是 GB2312 还…

机器学习(二) -- 数据预处理(2)

系列文章目录 机器学习&#xff08;一&#xff09; -- 概述 机器学习&#xff08;二&#xff09; -- 数据预处理&#xff08;1-3&#xff09; 机器学习&#xff08;三&#xff09; -- 特征工程&#xff08;1-2&#xff09; 未完待续…… 目录 系列文章目录 前言 四、【数…

三、C语言中的分支与循环—while循环 (5)

本章分支结构的学习内容如下&#xff1a; 三、C语言中的分支与循环—if语句 (1) 三、C语言中的分支与循环—关系操作符 (2) 三、C语言中的分支与循环—条件操作符 与逻辑操作符(3) 三、C语言中的分支与循环—switch语句&#xff08;4&#xff09;分支结构 完 本章循环结…

部署上传漏洞的靶场环境upload-labs

1、工具介绍 upload-labs是一个使用php语言编写的&#xff0c;专门收集渗透测试和CTF中遇到的各种上传漏洞的靶场。旨在帮助大家对上传漏洞有一个全面的了解。目前一共20关&#xff0c;每一关都包含着不同上传方式。 upload-labs靶场开源地址&#xff1a;&#xff1a;https://…

Android14之禁掉Selinux的两种方式(一百七十四)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

经典卷积神经网络-ResNet

经典卷积神经网络-ResNet 一、背景介绍 残差神经网络(ResNet)是由微软研究院的何恺明、张祥雨、任少卿、孙剑等人提出的。ResNet 在2015 年的ILSVRC&#xff08;ImageNet Large Scale Visual Recognition Challenge&#xff09;中取得了冠军。残差神经网络的主要贡献是发现了…

为什么python值得学习,python的应用领域。

文章目录 前言Web 应用开发自动化运维网络爬虫数据分析科学计算人工智能胶水语言Python技术资源分享1、Python所有方向的学习路线2、学习软件3、入门学习视频4、实战案例5、清华编程大佬出品《漫画看学Python》6、Python副业兼职与全职路线 前言 说起编程语言&#xff0c;Pyth…

【电商项目实战】购物车完善

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《电商项目实战》。&#x1f3af;&#x1f3af; &am…

ElasticSearch的DSL查询语法解析

Elasticsearch提供了基于ISON的DSL (Domain Specific Lanquage)来定义查询。 目录 一、常见查询类型 二、DSLQuery基本语法 三、全文检索查询 3.1 match查询&#xff1a;会对用户输入内容分词&#xff0c;常用于搜索框搜索 &#xff0c;语法&#xff1a; 3.2 multi match…

基于矩阵乘的CUDA编程优化过程

背景&#xff1a;网上很多关于矩阵乘的编程优化思路&#xff0c;本着看理论分析万遍&#xff0c;不如实际代码写一遍的想法&#xff0c;大概过一下优化思路。 矩阵乘的定义如下&#xff0c;约定矩阵的形状及存储方式为: A[M, K], B[K, N], C[M, N]。 CPU篇 朴素实现方法 按照…

canvas绘制网格线示例

查看专栏目录 canvas示例教程100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

自动化测试:等待方式

在自动化测试中&#xff0c;等待是一个重要的技术&#xff0c;用于处理页面加载、元素定位、元素状态改变等延迟问题。 等待能够确保在条件满足后再进行后续操作&#xff0c;提高自动化测试的稳定性以及可靠性。 等待方式&#xff1a;显示等待、隐式等待、线程睡眠 1. 显式等…

【学习笔记】环论

子环环同态理想单位元&#xff08;乘法单位元&#xff09;环与子环的单位元无必然关系,即子环不一定有单位元&#xff0c;有也不一定和环的单位元相同 比如 Z 6 Z_6 Z6​有单位元1&#xff0c;其子环 ( 2 ) (2) (2)单位元为4;Z有单位元1&#xff0c;其子环2Z没有单位元若R有单位…

B站不赚钱、“芒果”赚钱难,视频“后浪”火拼跨年夜

又是一年跨年时。 各大视频平台跨年晚会展开火拼&#xff0c;今年谁是赢家&#xff1f; 作为视频“后浪”&#xff0c;芒果超媒&#xff08;300413.SZ&#xff09;、哔哩哔哩&#xff08;09626.HK&#xff0c;下称“B站”&#xff09;此前相继公布了2023年三季报&#xff0c;…

什么是缓存、为什么要用缓存、缓存分类、缓存测试、缓存更新、缓存设计考虑点、缓存测试点

一、缓存 缓存是一种将数据存储在高速缓存中的技术&#xff0c;它可以提高应用程序的性能和响应速度。 二、 为什么要用缓存 1. 高性能(主要目的) 查询耗时&#xff0c;但变化少&#xff0c;又有很多读请求情况下&#xff0c;可以将查询结果放到缓存中。减少对数据库的压力&…

2024/1/2作业

成果 代码 main .c #include "sqi.h" #include "si7006.h" unsigned int num[10] {0xFC, 0x60, 0xDA,0xF2,0x66,0xB6,0xBE,0XE0,0xFE,0xF6}; //unsigned int wei[5]{0x10,0x20,0x40,0x80,0xF0}; unsigned short hum; short tem; extern void printf(…

C++day5作业

全局变量&#xff0c;int monster 10000;定义英雄类hero&#xff0c;受保护的属性string name&#xff0c;int hp,int attck&#xff1b;公有的无参构造&#xff0c;有参构造&#xff0c;虚成员函数 void Atk(){blood-0;}&#xff0c;法师类继承自英雄类&#xff0c;私有属性 …

HarmonyOS 组件通用属性之通用事件 文档参数讲解(按键事件)

最后 我们来看按键事件 按键事件是指 键盘 遥控器等按键设备交互的时候触发的事件 这边 我们还是以按钮为例 给大家演示一下 我们 event 参数的类型 变成了 KeyEvent 我们还是先看看 里面都有些什么东西 还是打开编辑器文档 组件通用信息 通用事件 下面的 按键事件 首先 这…