反射机制(复习)

news2025/8/14 18:55:27

反射机制

  • 反射机制
      • 定义
      • 反射机制的功能
      • 反射机制主要的API
      • 反射机制演示
      • 对 Class 的理解
      • Class实例获取的四种方式
      • Class 对应内存结构说明
      • 加载Properties文件的俩种方式
      • 调用运行时类的结构
        • 调用运行时类的指定属性
        • 调用运行时类指定的方法
        • 调用运行时类指定的构造器
      • 反射的应用:动态代理

定义

动态调用类、对象的信息,方法和属性

反射机制的功能

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时获取泛型信息
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时处理注解
  • 生成动态代理

反射机制主要的API

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Constructor:代表类的构造器

反射机制演示

public class ReflectionTest {
    @Test
    public void test1() {
        // p普通方式创建对象,以及调用属性、方法
        Person p1 = new Person("Tom",21);
        p1.setName("张三");
        p1.methods1("aa");
        // 无法调用私有方法
        // p1.methods2();
        System.out.println(p1);
    }
    // 通过反射机制创建对象、调用属性、方法
    @Test
    public void test2() throws Exception{
        Class<Person> aClass = Person.class;
        // 通过反射调用构造器方法
        Constructor<Person> con1 = aClass.getConstructor(String.class, int.class);
        Person p1 = con1.newInstance("Mack", 21);
        System.out.println(p1);

        // 通过反射机制调用方法
        Method methods1 = aClass.getDeclaredMethod("methods1", String.class);
         methods1.invoke(p1,"哈哈哈");

         // 通过反射机制调用私有方法
        Method methods2 = aClass.getDeclaredMethod("methods2");
        methods2.setAccessible(true);
        methods2.invoke(p1);

        // 通过反射机制调用普通
        Field age = aClass.getDeclaredField("age");
        age.set(p1,22);
        System.out.println(p1);

         // 通过反射机制调用私有属性
        Field name = aClass.getDeclaredField("name");
        name.setAccessible(true);
        name.set(p1,"李四");
        System.out.println(p1);


    }
}
 class Person {
    private String name;
    public int age ;

    public Person() {
    }

    public void methods1(String s) {
        System.out.println("我是一个public方法");
    }
    private void methods2(){
        System.out.println("我是一个私有方法");
    }

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

    private Person(String name) {
        this.name = name;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

疑问1 : 既然可以 new 对象为什么还要有 反射机制 ? 使用哪种方式呢?

平常肯定是使用 new 的方式,但是在某些应用场景下,还是需要使用反射机制。比如:我们在编译期不确定要创建哪个对象,要执行哪个方法。就需要反射机制的动态调用。

比如在 项目中,在项目启动时前端发送请求,这个时候后端就需要根据请求路径动态判断需要执行哪个 Servlet ,这就需要反射机制

以下代码截取为 javaweb 书城的 反射机制代码:

image-20221106151821384

对 Class 的理解

编写完代码后,由前端编译器对源代码文件编译成 .class 字节码文件。

通过 类加载器 ClassLoader 将class文件加载到内存中去。

这个被加载到内存中的类就被称为运行时类,这个类就是Class的一个实例

Person p1 = new Person();

p1 是 Person 的实例,而Person 是 Class的实例。(前提是编译运行之后)

因此也可以说一个 Class实例对应一个运行时类

Class实例获取的四种方式

  • 调用运行时类的属性: .class
  • 通过运行时类的对象,调用getClass()
  • 通过Class的静态方法,Class.forName(String classPath)
  • 通过类加载器中的 loadClass()
  @Test
    public void test1() throws ClassNotFoundException {
        // 第一种方式: 调用运行时类的属性
        Class<Person> clazz = Person.class;
        System.out.println(clazz);
        //第二种方式:通过运行时类的对象,调用 getClass
        Person person = new Person();
        Class clazz1 = person.getClass();
        System.out.println(clazz1);
        //第三种方式:调用Class的静态方法 forName
        Class<?> clazz2 = Class.forName("Person");
        System.out.println(clazz2);
        // 第四种方式:通过ClassLoader的 loadClass方法
        ClassLoader classLoader = ReflectionTest2.class.getClassLoader();
        Class<?> clazz3 = classLoader.loadClass("Person");
        System.out.println(clazz3);
    }

Class 对应内存结构说明

哪些类型可以有Class对象?

(1)class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
(2)interface:接口
(3)[]:数组
(4)enum:枚举
(5)annotation:注解@interface
(6)primitivetype:基本数据类型
(7)void

    @Test
    public void test4() {
        Class s1 = Object.class;
        Class s2 = Comparable.class;
        Class s3 = String[].class;
        Class s4 = int[][].class;
        Class s5 = ElementType.class;
        Class s6 = Override.class;
        Class s7 = int.class;
        Class s8 = void.class;
        Class s9 = Class.class;

        int[] a = new int[10];
        int[] b = new int[100];
        Class s10 = a.getClass();
        Class s11 = b.getClass();
        // 只要数组的元素类型与维度一样,就是同一个Class
        System.out.println(s10 == s11);
    }

加载Properties文件的俩种方式

第一种方式:使用 FileInputStream方式

    @Test
    public void test() throws Exception{
        // 第一种方式: 文件路径默认在module下
        Properties prop = new Properties();
        FileInputStream is = new FileInputStream("jdbc.properties");
        prop.load(is);

        String name = prop.getProperty("name");
        String age = prop.getProperty("age");
        System.out.println(name + "  "+age);
    }

第二种方式:使用CLassLoader加载

    @Test
    public void test1() throws IOException {
        // 第二种方式: 默认路径在 src 下
        Properties prop = new Properties();
        ClassLoader classLoader = this.getClass().getClassLoader();
        InputStream asStream = classLoader.getResourceAsStream("jdbc1.properties");
        prop.load(asStream);

        String name = prop.getProperty("name");
        String age = prop.getProperty("age");
        System.out.println(name + "  " + age);
    }

俩种方式的区别

第一中方式默认加载的文件路径在 此module 下

第二种方式默认加载文件路径在 src 目录下

第一种方式也可以加载src目录下,增加目录 路径就行了:

FileInputStream is = new FileInputStream("src/jdbc1.properties");

调用运行时类的结构

调用运行时类的指定属性

image-20221106174034649

俩个方法,第一个方法要求类的属性权限至少是 public,第二个要求属性权限至少是private,一般都使用第二种。

    // 获取运行时类的属性
    @Test
    public void test() throws Exception {
        Class<Person> clazz = Person.class;
        // 创建运行时类对象
        Person person = clazz.newInstance();
        // 获取指定属性
        Field name = clazz.getDeclaredField("name");
        // 打破封装
        name.setAccessible(true);
        // 设置属性
        // 第一个参数为属性的对象,第二个参数为属性值
        name.set(person,"张三");
        // 获取属性
        Object name1 =name.get(person);
        System.out.println(name1);
    }

调用运行时类指定的方法

image-20221106174952819

使用情况和属性的一样

    @Test
    public void test1() throws Exception {
        Class<Person> clazz = Person.class;
        Person person = clazz.newInstance();
        // 获取指定方法: 第一个参数为方法名,第二个参数为方法形参类型
        Method methods1 = clazz.getDeclaredMethod("methods2",String.class);
        methods1.setAccessible(true);
        // invoke:执行方法,invoke方法的返回值是调用方法的返回值
        methods1.invoke(person,"李四");
    }

调用运行时类指定的构造器

image-20221106175906863

和上面一样不写了

    // 调用运行时类的指定构造器
    @Test
    public void test2() throws Exception {
        Class<Person> clazz = Person.class;
        Person person = clazz.newInstance();
        Constructor<Person> constructor = clazz.getDeclaredConstructor(String.class);
        constructor.setAccessible(true);
        Person person1 = constructor.newInstance("李四");
        System.out.println(person1);
    }

反射的应用:动态代理

代理模式的原理

使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

  • 静态代理:特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能

  • 动态代理:是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。

  • 动态代理相比于静态代理的优点

    抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。

静态代理演示:

public class StaticProxyTest {

    interface ClothFactory {
        void createCloths();
    }

    // 代理类
static   class ProxyClothFactory implements ClothFactory{
        private ClothFactory clothFactory;

        public ProxyClothFactory(ClothFactory clothFactory) {
            this.clothFactory = clothFactory;
        }

        @Override
        public void createCloths() {
            System.out.println("代理工厂的一些准备工作");
            clothFactory.createCloths();
            System.out.println("代理工厂的一些后序工作");
        }
    }
    // 被代理类
  static  class NikeClothFactory implements ClothFactory{

        @Override
        public void createCloths() {
            System.out.println("NIKE 生成了一批衣服");
        }
    }

    public static void main(String[] args) {
        NikeClothFactory nikeClothFactory = new NikeClothFactory();
        ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nikeClothFactory);
        proxyClothFactory.createCloths();
    }
}

输出结果:

代理工厂的一些准备工作
NIKE 生成了一批衣服
代理工厂的一些后序工作

动态代理演示:

动态代理:通过动态生成代理类对象,去调用被代理类中的方法。

一般代理类就是一个接口,只提供方法,不提供具体的服务。在被代理类中提供服务。

  • 如何通过加载到内存中的被代理类,动态生成代理类及其对象?
  • 通过代理对象调用方法时,如何动态的去调用被代理类的方法?
/**
 *
 * Author: YZG
 * Date: 2022/11/6 18:25
 * Description: 动态代理:通过动态生成代理类对象调用被代理类中的方法
 */
public class DynamicProxyTest {
    public static void main(String[] args) {
        Man man = new Man();
        // 代理类的对象
        Human human = (Human) Man.ProxyFactory.getInstance(man);
        human.eat("麻辣烫");
        human.sleep(8);
    }

    // 代理类
    interface Human {
        void eat(String food);

        void sleep(int hours);
    }

    // 被代理类
    static class Man implements Human {

        @Override
        public void eat(String food) {
            System.out.println("男人吃了 " + food);
        }

        @Override
        public void sleep(int hours) {
            System.out.println("男人睡了 " + hours + "小时");
        }

        // 动态代理类
        static class ProxyFactory {
            /**
             * @description 调用此方法 生成一个代理类的对象
             * @date 2022/11/6 19:06
             * @param obj 被代理类的对象
             * @return java.lang.Object
             */
            public static Object getInstance(Object obj) {
                return Proxy.newProxyInstance(
                    	obj.getClass().getClassLoader(),
                        obj.getClass().getInterfaces(),
                        /**
                         * @description 动态调用被代理类中的方法。需要实现  InvocationHandler 接口
                         * @date 2022/11/6 19:07
                         * @param obj 被代理类的对象
                         * @return java.lang.Object
                         */
                        (Object proxy, Method method, Object[] args) -> {
                            // 动态调用被代理类对象中的方法
                            return method.invoke(obj, args);
                        });
            }
        }
    }

}

关于 Proxy.newProxyInstance 的三个参数: 被代理类的类加载器,被代理类实现的接口(代理类)、需要实现指定的接口

image-20221106192321497

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

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

相关文章

技术公开课|深度剖析 Java 的依赖管理,快速生成项目 SBOM清单

背景 近年来软件供应链安全风险涌现&#xff0c;无论是 Fastjson、Log4j 等基础组件的 0day&#xff0c;来源于开源的风险事件不断上升&#xff0c;对于研发以及安全同学来说&#xff0c;都是在不断的摸索建立有效的预防及解决机制&#xff0c;公开课将以风险治理为最终目标、…

Java -- 每日一问:谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?

典型回答 所谓隔离级别&#xff08;Isolation Level&#xff09;&#xff0c;就是在数据库事务中&#xff0c;为保证并发数据读写的正确性而提出的定义&#xff0c;它并不是 MySQL 专有的概念&#xff0c;而是源于ANSI/ISO制定的SQL-92标准。 每种关系型数据库都提供了各自特…

(STM32)从零开始的RT-Thread之旅--PWM驱动ST7735调光

上一章&#xff1a; (STM32)从零开始的RT-Thread之旅--SPI驱动ST7735(1) 上一章我们先用SPI读取到了LCD的ID&#xff0c;这一章则是使用PWM调光点亮屏幕&#xff0c;因为测试这块屏幕时&#xff0c;发现直接设置背光引脚为高好像无法点亮&#xff0c;好像必须使用PWM调光&…

信而泰自动化OSPFv2测试小技巧

OSPFv2协议简介 OSPFv2&#xff08;开放式最短路径优先版本2&#xff09;是互联网协议&#xff08;IP&#xff09;网络的路由协议。它使用链路状态路由&#xff08;LSR&#xff09;算法&#xff0c;并且属于在单个自治系统&#xff08;AS&#xff09;内运行的内部网关协议&…

Inter RealSense深度相机ROS驱动

文章目录知识目标1. 深度相机的分类及工作原理2. Inter RealSense D415相机知识目标 学习深度相机的分类和工作原理&#xff1b; 学习Intel RealSense D415相机硬件构成和工作原理。 1. 深度相机的分类及工作原理 深度相机&#xff08;可以测量物体到相机的距离&#xff09;…

Java三大特性篇之——多态篇(千字详解)

JAVA面向对象编程有三大特性&#xff1a;封装、继承、多态,在我们学习了继承后&#xff0c;我们将继续学习多态。 文章目录前言&#xff1a;什么是多态&#xff1f;一、多态实现二、再谈重写三、向上转移四、多态优缺点五、多态案例最后前言&#xff1a;什么是多态&#xff1f;…

MR场景直播-帮助企业高效开展更有意思的员工培训

阿酷TONY / 2022-11-18 / 长沙 MR场景直播、MR培训场景和内容呈现以及直播互动功能&#xff0c;帮助企业高效开展员工培训&#xff0c;让整个培训过程更高效~~~ MR场景直播有哪些有意思的地方呢&#xff1f;先来一个图&#xff1a; ▲ 模拟真实光照还原现实景 丰富培训场景&a…

SQL实用功能手册

SQL实用功能手册 SQL基础复习 SQL结构化查询语言&#xff0c;是一种访问和处理数据库的计算机语言 对数据库操作对表操作对数据进行CRUD操作操作视图、存储过程、索引 环境基础操作 安装mysql、启动mysql、配置环境变量检查mysql版本&#xff1a;mysql --version链接mysql…

开源共建 | Dinky 扩展批流统一数据集成框架 ChunJun 的实践分享

一、前言 ChunJun&#xff08;原FlinkX&#xff09;是一个基于 Flink 提供易用、稳定、高效的批流统一的数据集成工具&#xff0c;既可以采集静态的数据&#xff0c;比如 MySQL&#xff0c;HDFS 等&#xff0c;也可以采集实时变化的数据&#xff0c;比如 binlog&#xff0c;Ka…

(九)DateTime——PHP

文章目录第九章 Date & Time1 time()获取时间戳2 getDate()转换时间戳3 date()转换时间戳第九章 Date & Time 1 time()获取时间戳 time()函数返回的整数表示自1970年1月1日格林尼治标准时间午夜起经过的秒数。这一时刻称为UNIX历元&#xff0c;自那时起经过的秒钟数称…

Metabase学习教程:提问-1

创建交互式图表 可以通过使用查询生成器、构建模型或添加自定义目标来创建图表。供用户在Metabase中钻取数据。类似&#xff1a; 图1。放大特定类别和时间范围&#xff0c;然后查看构成图表上某个条形图的订单。 如果您只使用SQL编写过问题&#xff0c;那么您可能会忽略这样一…

颠覆IoT行业的开发神器!涂鸦智能重磅推出TuyaOS操作系统【程序员必备】

1 前言 作为降低 IoT 技术门槛的开发神器&#xff0c;TuyaOS 操作系统重磅发布 3.6.0 新版本啦&#xff01;针对设备安全、功耗、通信速率等关键功能&#xff0c;做了重大创新和优化升级。为了助力开发者更快速便捷地接入涂鸦IoT PaaS&#xff0c;并低门槛开发出有创意的智能单…

RabbitMQ初步到精通-第三章-RabbitMQ面板及环境搭建

第三章-RabbitMQ面板及环境搭建 1、RabbitMQ面板介绍 Rabbitmq安装完毕后&#xff0c;若是本地环境&#xff0c;打入&#xff1a;http://localhost:15672/#/ 进入到MQ的控制台页面中&#xff1a; 可以观察到此页面涉及的各个TAB&#xff0c;和我们前面介绍到的rabbitMQ架构中…

Docker入门学习笔记(狂神版)

下述笔记是自己花一天时间看B站狂神说Docker视频的笔记&#xff0c;下列的笔记是根据自己的实践的记录下来的&#xff0c;若想细学掌握Docker建议自行观看&#xff08;《Docker入门到精通》&#xff09;&#xff0c;去观看狂胜的视频记得三连支持一下。他的Docker讲解个人觉得是…

每日一个设计模式之【代理模式】

文章目录每日一个设计模式之【代理模式】☁️前言&#x1f389;&#x1f389;&#x1f389;&#x1f33b;模式概述&#x1f331;代理模式的实现&#x1f340;静态代理&#x1f340;动态代理&#x1f433;JDK代理&#x1f433;CGLib代理&#x1f340;拓展&#x1f433;虚拟代理&…

UE 5.1正式发布,有哪些值得一试的新功能?

UE 5.1正式发布&#xff0c;所以今天咱们就来看看最新版都具体更新和改进了哪些功能吧—— Nanite和Lumen Nanite和Lumen是UE 5.0版本更新的两个主要内容&#xff0c;UE 5.1则是对其进行进一步的改进。 Nanite添加了对双面材质和新的可编程光栅化程序的支持&#xff0c;可以通…

认识前端闭包

1、前言&#xff1a;&#xff08;先介绍一下函数的存储原理&#xff09; &#xff08;1&#xff09;基本函数存储原理&#xff1a; 首先我们定义一个函数&#xff0c;然后调用&#xff0c;如下&#xff1a; <script>function test(){let name yiyiconsole.log(name)}t…

未来的产品经理,需要什么样的原型设计工具?

我和原型工具的往事 作为一只工龄十年的产品汪&#xff0c;我一直在使用原型设计工具——摹客 &#xff0c;对于摹客可谓耳熟能详。 初次听说&#xff0c;是产品社群里的点赞安利&#xff1a;做移动端&#xff0c;用摹客。 当时正值2015年&#xff0c;移动端热到不行&#x…

【动态规划】字串中回文的个数

一、题目描述 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。 注&#xff1a; 回文字符串 是正着读和倒过来读一样的字符串。子字符串 是字符串中的由连续字符组成的一个序列。具有不同开始位置或结束位置的子串&#xff0c;即使是由相同的字符…

Matlab论文插图绘制模板—水平三维柱状图(渐变)

在之前的文章中&#xff0c;分享了Matlab水平三维柱状图的绘制模板。 高度赋色的水平三维柱状图&#xff1a; 这次再来分享一下渐变赋色的水平三维柱状图的绘制模板。 先来看一下成品效果&#xff1a; 特别提示&#xff1a;Matlab论文插图绘制模板系列&#xff0c;旨在降低大家…