java 反射及代理模式初步学习

news2024/11/26 18:21:48

java 反射及代理模式初步学习

0. 什么是反射?

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。[摘自百度百科]

1. 反射的用途

反射通常由需要检查或修改Java虚拟机中运行的应用程序的运行时行为的程序使用。这是一个相对高级的功能,只应由对语言基础有很深了解的开发人员使用。考虑到这一警告,反射是一种强大的技术,可以使应用程序执行原本不可能的操作。

2. 反射的优缺点

  1. 优点:
    a. 可扩展性
    应用程序可以通过使用其完全限定的名称创建可扩展性对象的实例来使用外部用户定义的类。
    b. 类浏览器和可视化开发环境
    类浏览器需要能够枚举类的成员。可视化开发环境可以受益于利用反射中可用的类型信息来帮助开发人员编写正确的代码。
    c. 调试器和测试工具
    调试器需要能够检查类的私有成员。测试工具可以利用反射来系统地调用在类上定义的可发现的集合API,以确保测试套件中的代码覆盖率很高。
  2. 缺点:
    反射功能强大,但不应随意使用。如果可以在不使用反射的情况下执行操作,那么最好避免使用它。通过反射访问代码时,应牢记以下注意事项。
    a. 性能开销
    由于反射涉及动态解析的类型,因此无法执行某些Java虚拟机优化。因此,反射操作的性能要比非反射操作慢,因此应避免在对性能敏感的应用程序中经常调用的代码段中。
    b. 安全限制
    反射需要运行时许可,而在安全管理器下运行时可能不存在。对于必须在受限的安全上下文(例如Applet)中运行的代码,这是一个重要的考虑因素。
    c. 暴露内部细节
    由于反射允许代码执行在非反射代码中是非法的操作,例如访问私有字段和方法,因此使用反射可能会导致意外的副作用,这可能会使代码无法正常工作并可能破坏可移植性。反射性代码破坏了抽象,因此可能会随着平台的升级而改变行为。

以上内容引用百度百科或翻译自java官方文档,使大家对于Java反射有一个基本的认识,接下来我们开始学习java反射的基本使用。

3. Java反射基本使用

     1. 首先我们创建一个类,然后分别使用正常手段和反射方式获取类的实例
     public class Person {
    String name;
    private int age;

    // 无参构造
    public Person ()
    {
        
    }
    
    // 有参构造
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }


    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    
    // public 方法
    public void printName()
    {
        System.out.println("printName = " + name);
    }
    
    // private 方法
    private void printAge()
    {
        System.out.println("printAge = " + age);
    }
}

这个类共有两个属性,两个构造方法,set,get方法以及一个公共的printName方法,一个私有的printAge方法,下面我们就分别使用正常方法和反射方法获取该类的对象

一. 正常的方式
	Person person = new Person();   // 这种方式就不多说了,大家都会的
二. 反射方式
	// 使用反射获取类对象的步骤如下
	// 1. 获取类
	// 2. 调用newInstance方法实例化对象
	// 获取类的方式有三种
	// 1. 类名.class
	// 2. 对象名.getClass();
	// 3. Class.forName("类的全路径名");
	// 以下例子讲解了反射的各种用法,其中包括:
	// 1. 获取类
	// 2. 获取类加载器(这个后面再说,先了解)
	// 3. 获取方法列表(public、private)
	// 4. 获取属性列表(public、private)
	// 5. 获取构造器列表
	// 6. 获取指定的方法,属性
	具体请看以下代码:


public class main {


    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {

       // 1. 正常的手段
        Person person = new Person();

        // 2. 反射

        // 第一种拿到类的方式
        Class cls = Person.class;
        // 第二种拿到类的方式
        Class cls1 = person.getClass();
        // 第三种拿到类的方式
        Class cls2 = Class.forName("Person");


        // 获取实例, 使用Class.newInstance方法来实例化对象
        Person person1 = (Person) cls.newInstance();
        person1.setName("jiangc");
        person1.printName();

        // 类加载器,获取类加载器的方式,Class.getClassLoader
        ClassLoader classLoader = person1.getClass().getClassLoader();
        System.out.println(classLoader);

        System.out.println("------------------------获取公共方法,包括从父类继承   通过getMethods方法-----------------------------------------------------");
        // 获取公共方法,包括从父类继承   通过getMethods方法
        Method[] methods = cls.getMethods();
        // 打印一下
        for (int i = 0; i < methods.length; i++) {
            System.out.println(methods[i].getName() + "()");
        }
        System.out.println("------------------------获取类的所有方法,包括私有方法,只能是本类的 通过getDeclareMethods方法----------------------------------------------------");
        // 获取类的所有方法,包括私有方法,只能是本类的 通过getDeclareMethods方法
        Method[] declaredMethods = cls.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod.getName() + "()");
        }
        System.out.println("------------------------获取指定方法 通过getMethod方法, 第一个参数是方法名,后面是可变参数,是参数的类型-----------------------------------------------------");
        // 获取指定方法 通过getMethod方法, 第一个参数是方法名,后面是可变参数,是参数的类型
        Method setName = cls.getMethod("setName", String.class);
        // 执行方法 通过invoke 第一个是对象,后面是方法的参数
        setName.invoke(person1, "lalala");
        person1.printName();
        // 其中注意的一个点,类似于int类型的在传入类型的时候使用int.class 这种方式,如下所示
        Method setAge = cls.getMethod("setAge", int.class);
        setAge.invoke(person1, 18);
        System.out.println("age = " + person1.getAge());

        // 访问私有方法有一些不同, 如果直接invoke会报错,因为没有访问权限,在调用私有方法的时候需要先调用setAccessible方法,打开访问权限
        Method printAge = cls.getDeclaredMethod("printAge");

        printAge.setAccessible(true);

        printAge.invoke(person1);
        System.out.println("-------------------获取字段 使用getFields 方法----------------------------------------------------------");

        // 获取字段 使用getFields 方法
        Field[] fields = cls.getFields();
        for (Field field : fields) {
            System.out.println(field.getName());
        }
        System.out.println("-------------------这里只有一个public修饰的字段,使用getDeclareFields方法可以获取私有的字段----------------------------------------------------------");

        // 这里只有一个public修饰的字段,使用getDeclareFields方法可以获取私有的字段
        Field[] declaredFields = cls.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField.getName());
        }
        System.out.println("--------------------获取指定字段,和方法类似,这里使用的是getField方法, 传入字段名---------------------------------------------------------");
        // 获取指定字段,和方法类似,这里使用的是getField方法, 传入字段名
        Field name = cls.getField("name");
        System.out.println("打印:");
        System.out.println("字段的名字 :" + name.getName());
        // 打印字段的值
        System.out.println("字段的值:" + name.get(person1));
        // 修改public字段的值
        // 修改字段的值
        name.set(person1, "yayaya");
        System.out.println(name.get(person1));
        Object o = name.get(person1);

        // 修改私有字段的值
        System.out.println("修改私有字段的值, 和私有方法一样,无论访问还是修改,都需要调用setAccessible方法来打开权限");
        // 获取私有字段
        Field age = cls.getDeclaredField("age");

        age.setAccessible(true);
        System.out.println("age原本的值:" + age.get(person1));
        // 修改age
        age.set(person1, 27);
        System.out.println("修改之后的值:" + age.get(person1));
        // 大家看到,前面通过反射拿到的对象都是调用的newInstance方法,
        //这个方法我们没有传参数,Person有一个有参构造,那么我们怎么通过有参构造来实例化对象呢
        // 使用Constructors()方法获取所有的构造方法
        System.out.println("-----------------------------------------------------------------------------");
        System.out.println("通过getConstructors方法获取构造方法的列表");
        Constructor<Person>[] constructors = (Constructor<Person>[]) cls.getConstructors();
        for (Constructor<Person> constructor : constructors) {
            System.out.println(constructor);
        }

        // 获取指定名称的构造方法
        System.out.println("获取指定构造器实例化对象");
        Constructor<Person> constructor = (Constructor<Person>) cls.getConstructor(String.class, int.class);
        Person xixixi = (Person) constructor.newInstance("xixixi", 23);
        xixixi.printName();
        // 其他获取私有的啊什么的大家自己去尝试

        // 以上讲解了如何使用反射获取类的public和private的方法、属性,列表,获取public和private的指定方法,属性,以及调用方法,还有获取指定构造器实例化的使用方式。反射的知识就讲到这里。



    }
}

4. 反射在代理模式中的使用

首先我们先看一下代理模式,什么是代理模式,举个例子:代购的公司,房产中介公司,都可以看作代理,例如说你要买海外的东西,有两种方式:1. 自己直接联系海外商家,直接买,2. 找代购,把自己的需求告诉代购,代购全权代理,你不需要和商家打交道,这就是代理
我们先来看一下代理模式的类的关系图:
这里是代理模式的类图
说一下这个类的关系,Subject类主要定义了一些公共方法,然后真实类和代理类去实现这个类,同时代理类拥有真实类的引用,从而调用真实类的方法。

代理模式有两种:1. 静态代理 2. 动态代理
我们先看静态代理

首先,想象一个场景,有一个房产中介aProxy,他可以为客户提供租房服务,客户aClient找他租房,中介aProxy找房东,中介为客户提供全套服务,包括合同,打扫等服务,下面我们根据代理模式的类图来实现一下

  1. 公共类,提供接口
/**
 * 公寓类,定义公共方法(对应抽象类:Subject)
 */
public interface Apartment {
    // 租房接口,提供一个参数:租房面积
    public void renting(int area);
}
	
3. 房东提供租房
/**
 * 房产中介(对应上面类图中的真实类:Goodscompanies)
 */
public class Intermediary implements Apartment{
    @Override
    public void renting(int area) {
        System.out.println("房屋出租面积为:" + area + "的房屋");

    }
}
4. 中介代理房东

/**
 * 代理人(对应上面的Proxy)
 */
public class ProxyPeople implements Apartment{
    // 真实对象的引用
    private Intermediary intermediary;


    /**
     * 代理自己的方法(增强方法)
     */
    private void conclude_contract()
    {
        System.out.println("代理签合同");
    }


    /**
     *  代理自己的方法(增强方法)
     */
    private void sweep()
    {
        System.out.println("代理打扫房间");
    }
    @Override
    public void renting(int area) {

        conclude_contract();
        // 调用真实的对象的方法
        intermediary.renting(area);
        sweep();
    }
}

这时候,客户想买一个新房子,而这个租房子的代理人没有能力提供买房子的服务(大家这里不要抠细节,我们假设这个中介就只能处理租房的事情),而另外一个中介可以提供买房的信息,所以,客户买房子就只能换一个代理这里记为:ProxyRealty,提供新房的实体其实是房地产开发商,所以关系是,买房的代理通过开放商向客户提供买房服务,请看下面的代码:

/**
 * 房产类接口
 */
public interface Brealty {
    public void renting(int area);
}

/**
 * 开发商
 */
public class realty implements Brealty{
    @Override
    public void renting(int area) {
        System.out.println("客户买了:" + area + "面积的房子");
    }
}

/**
 * 提供买房服务代理类
 */
public class ProxyRealty implements Brealty{

    // 提供房子的实体类
    private realty re;

    public ProxyRealty(realty re) {
        this.re = re;
    }

    private void see_apartment()
    {
        System.out.println("带着顾客看房子...");
    }

    private void sign_contract ()
    {
        System.out.println("和客户签合同");
    }

    private void pay()
    {
        System.out.println("交首付");
    }

    private void handing_room()
    {
        System.out.println("交房");
    }
    @Override
    public void renting(int area) {
        see_apartment();
        re.renting(200);
        sign_contract();
        pay();
        handing_room();
    }
}

大家想一下,假如这时候客户买完房子了,想装修,这时候通过网络又找到另外一个代理人,全权交给这个人帮自己装修,代码和上面类似,这里就不写了,大家有感觉了吗?这就是静态代理,大家可以对照类图和代码例子理解一下。

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

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

相关文章

小程序组件

swiper swiper 和 swiper-item 结合使用&#xff0c;&#xff0c; swiper有默认高度 300rpx 属性&#xff1a; autoplay &#xff1a; 自动播放circular &#xff1a; 循环播放indicator-dots &#xff1a; 显示指示点indicator-active-color &#xff1a; 轮播选中的颜色 &…

SpringBoot整合钉钉消息推送(四十四)

从头开始&#xff0c;并不意味着失败&#xff0c;相反&#xff0c;正是拥抱成功的第一步&#xff0c;即使还会继续失败 上一章简单介绍了 SpringBoot 发送邮件(四十三), 如果没有看过,请观看上一章 钉钉消息推送&#xff0c; 官方文档: https://open.dingtalk.com/document/gr…

3.结构化的数学思想

前言 今天在复习概率论的公理化过程中&#xff0c;我发现它的公理其实也是人为定义的&#xff0c;为什么我会这么想呢&#xff1f;这是因为我曾听过严伯均在为什么诺贝尔奖没有数学讲曾说过数学是一门无法证伪的学科&#xff0c;甚至不能算是科学&#xff0c;而诺贝尔设置这个…

超详细的Linux环境下使用git上传代码教程(gitee版)

git是一个版本控制器&#xff0c;我们使用它上传我们以前写过的代码给他进行托管&#xff0c;更为方便以后找到&#xff0c;同时也方便我们找到我们每次更改了什么。 创建仓库 创建完成后界面&#xff1a; 接下来复制我们创建的仓库的地址&#xff1a; 使用 git 命令行 安装…

BGP路由策略,IPV6

下一跳不变 从EBGP来的路由,传给|BGP S居时,下一跳不变 解决方案: 水平分割 ∷:AS内防环 从|BGP来的路由,不会传给IBGP邻居 1全互联 2路由反射器 3联盟 BGP选路原则: 当BGP 由表存在多条相同路由,会产生多个转发路径,BGP 会根据这些路由的属性,选择一条最优…

使用 C 语言验证非均匀概率的离散事件在样本数量足够大时,符合正态分布曲线(通过生成一个PPM格式的图像)

我想写本文的原因是看到著名数学科普账号 3Blue1Brown 发布的【官方双语】但是什么是中心极限定理&#xff1f;中提到&#xff1a;不论这个离散型事件的各种情况概率是不是平均的&#xff0c;当数量一定大时&#xff0c;还是会符合正态分布曲线。我就想自己试试看是不是这种情况…

深入篇【C++】类与对象:const成员与Static成员

深入篇【C】类与对象&#xff1a;const成员与Static成员 ⏰<const成员>&#x1f553;1.权限&#x1f550;2.规则&#x1f552;3.思考&#xff1a; ⏰<Static成员>&#x1f551;1.概念&#x1f557;2.特性&#x1f555;3.思考&#xff1a; ⏰<const成员> &am…

从零开始 Spring Boot 29:类型转换

从零开始 Spring Boot 29&#xff1a;类型转换 图源&#xff1a;简书 (jianshu.com) PropertyEditor Spring使用PropertyEditor进行String和具体类型之间的转换&#xff1a; public interface PropertyEditor {void setValue(Object value);Object getValue();String getAsT…

第五章 面向对象-7.hashCode()和toString()

hashCode()和toString() hashCode() hashCoed 的特性&#xff1a; &#xff08;1&#xff09;HashCode的存在主要是用于查找的快捷性&#xff0c;如Hashtable&#xff0c;HashMap等&#xff0c;HashCode经常用于确定对象的存储地址&#xff1b; &#xff08;2&#xff09;如果…

华为OD机试真题 Java 实现【统一限载货物数最小值】【2023Q1 200分】

一、题目描述 火车站附近的货物中转站负责将到站货物运往仓库&#xff0c;小明在中转站负责调度 2K 辆中转车(K辆干货中转车&#xff0c;K 辆湿货中转车)货物由不同供货商从各地发来&#xff0c;各地的货物是依次进站&#xff0c;然后小明按照卸货顺序依次装货到中转车&#x…

智能床垫市场调研分析报告

文章目录 一、简介&#xff08;1&#xff09;电动床&#xff08;2&#xff09;气垫床 二、使用人群三、睡姿四、实用性 一、简介 &#xff08;1&#xff09;电动床 电动床之下又分成了分体、连体和床头分体。分体电动床是指床垫与床底座分开的电动床&#xff1b;连体的则是床垫…

数据结构-外部排序-(多路归并排序、败者树、置换选择排序、最佳归并树)

目录 一、外部归并排序 二、败者树 三、置换选择排序 四、最佳归并树 一、外部归并排序 16个块&#xff0c;先每个块读入内存进行排序在输出回来&#xff0c;进行16次读和16次写 两两归并&#xff0c;第一趟如下 在两两归并 时间分析 外部排序时间开销读写外存时间内存排序时…

C语言基础知识:函数的声明和使用

目录 函数的声明 1.定义顺序 2.函数的声明 3.函数的声明格式 多源文件开发 1.为什么要有多个源文件 2.将sum函数写到其他源文件中 3.在main函数中调用sum函数 4.编译所有的源文件 5.链接所有的目标文件 #include 1.#include的作用 2.#include可以使用绝对路径 3.#…

Linux免交互操作

免交互操作 Here DocumentExpect工具 Here Document Here Document概述 使用I/O重定向的方式将命令列表提供给交互式程序或命令&#xff0c;比如 ftp 、cat 或 read 命令。Here Document 是标准输入的一种替代品&#xff0c;可以帮助脚本开发人员不必使用临时文件来构建输入信息…

docker搭建Elasticsearch集群

这里写目录标题 1.拉取es镜像2.配置配置文件3.启动容器4.启动过程中遇到的问题5.查看容器启动情况 1.拉取es镜像 docker pull docker.elastic.co/elasticsearch/elasticsearch:7.17.0版本根据自己需求进行拉取&#xff0c;我这边选择的是7.17.0&#xff0c;不同版本配置可能稍有…

ANR原理篇 - Input超时机制

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 文章目录 系列文章目录前言一、事件分发流程1.1 事件分发流程概览1.2 InputDispatcher 三、ANR触发流程超时重…

ANR原理篇 - service/broadcast/provider超时机制

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 文章目录 系列文章目录前言一、Service超时机制1.1 埋炸弹1.1.1 AS.realStartServiceLocked1.1.2 AS.bumpSer…

三大基础排序算法——我欲修仙(功法篇)

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️我欲修仙】 学习名言&#xff1a;莫等闲、白了少年头&#xff0c;空悲切。——岳飞 系列文章目录 第一章 ❤️ 学习前的必知知识 第二章 ❤️ 二分查找 文章目录 系列文章目录前言&#x1f697;&…

Netty实战(三)

Netty的组件和设计 一、Channel、EventLoop 和 ChannelFuture1.1 Channel 接口1.2 EventLoop 接口1.3 ChannelFuture 接口 二、ChannelHandler 和 ChannelPipeline2.1 ChannelHandler 接口2.2 ChannelPipeline 接口2.3 编码器和解码器2.4 抽象类 SimpleChannelInboundHandler 三…

suricata中DPDK收发包源码分析2

《suricata中DPDK收发包源码分析1》中分析了整体的DPDK收发包框架代码&#xff0c;今天我们继续来深入了解一下一些细节方面的问题。 目录 Q1&#xff1a;收发包线程模式在代码中是怎样确定的&#xff1f; Q2: DPDK库的初始化rte_eal_init在哪里调用的&#xff1f; Q3: 对网…