Android Java反射与Proxy动态代理详解与使用基础篇(一)

news2024/11/17 21:22:42

一、介绍

什么是反射?

       反射是java语言的一个特性,它允程序在运行时(注意不是编译的时候)来进行自我检查并且对内部的成员进行操作。

反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意方法和属性,这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

为什么要用反射?

Java Reflection API简介

Java Reflection功能非常强大,并且非常有用,比如:

  • 获取任意类的名称、package信息、所有属性、方法、注解、类型、类加载器等

  • 获取任意对象的属性,并且能改变对象的属性

  • 调用任意对象的方法

  • 判断任意一个对象所属的类

  • 实例化任意一个类的对象

  • 通过反射我们可以实现动态装配,降低代码的耦合度,动态代理等。

  在JDK中,主要由以下类来实现Java反射机制,这些类(除了第一个)都位于java.lang.reflect包中

  Class类:代表一个类,位于java.lang包下。

  Field类:代表类的成员变量(成员变量也称为类的属性)。

  Method类:代表类的方法。

  Constructor类:代表类的构造方法。

  Array类:提供了动态创建数组,以及访问数组的元素的静态方法。

二、反射的使用

1、创建类对象

正常我们创建类对象都是通过new关键字来创建,但是我们还有其他两种方式

  • 通过 Class 对象的 newInstance() 方法

  • 通过 Constructor 对象的 newInstance() 方法

基础数据:

package com.example.mylibrary.ref;

public class MyPerson {
    private String name = "default";
    public int age = 2;
    protected boolean sex;


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

    protected MyPerson(int age) {
        this.age = age;
    }

    public MyPerson() {

    }

    public MyPerson(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;
    }

    private String getPrivateName() {
        return name;
    }

    protected int getProtectAge() {
        return age;
    }
}

1.1通过 Class 对象的 newInstance() 方法

Class cls=Class.forName("com.example.mylibrary.ref.MyPerson");

MyPerson person=(MyPerson) cls.newInstance();

1.2通过 Constructor 对象的 newInstance() 方法

Constructor constructor=cls.getConstructor();
MyPerson myPerson=(MyPerson) constructor.newInstance();

以上都是构造器无默认参数,如果有的话1.1已无法满足,且1.1的方法在java9的时候已过期,接下我们主讲1.2通过Constructor 创作对象

有参构造:

1、public Constructor<T> getConstructor(Class<?>... parameterTypes) 

        要指定构造器的参数类型,必须一一对其

2、public T newInstance(Object... initargs)

  在获取对象时,需要传入默认参数

Constructor constructor=cls.getConstructor(String.class,Integer.TYPE);
MyPerson myPerson=(MyPerson) constructor.newInstance("你好",12);

1.3.获得成员变量Field

获取成员变量有四种:

1、Field[] fields = cls.getFields();
2、Field field = cls.getField("age");
3、Field[] dcfis = cls.getDeclaredFields();
4、Field dfied = cls.getDeclaredField("name");

getFields:获取所有public的属性变量

getField:获取指定为public的属性变量对象

getDeclaredFields:获取所有变量,包括public、protect、private

getDeclaredField:获取任性一个属性对象,包括public、protect、private

看如下debug日志:

注意:

1.通过getDeclared获取的是全部,如果方法前面没有Declared基本获取的就是public

2.如果field设置value异常不生效,需要设置field.setAccessible(true)

 1.4构造器的获取

构造器获取区分public和全部,通过如下方式获取

Constructor[] construPublic=  cls.getConstructors();//public
Constructor[] consrAll=cls.getDeclaredConstructors();//all

Log.log(construPublic.length+"");
Log.log(consrAll.length+"");

 

1.5.类的方法Method

Method是class中的方法,方法的获取如下


1.Method[] methods = cls.getMethods();

这种获取到的方法是当前类下可用的所有public方法,包括class自身的一些方法

getMethods=wait
getMethods=equals
getMethods=toString
getMethods=hashCode
getMethods=getClass
getMethods=notify
getMethods=notifyAll

2.Method[] methodDecs = cls.getDeclaredMethods();

这种获取的是当前类下的所有方法,不包括class自身提供的,只有用户定义的所有类型

getDeclaredMethods=getName
getDeclaredMethods=setName
getDeclaredMethods=getAge
getDeclaredMethods=setAge
getDeclaredMethods=getProtectAge
getDeclaredMethods=getPrivateName

1.6. 如何调用Method

先准备一个对象:MyPerson person=new MyPerson();

1.无参数调用

Method method_getAge = cls.getMethod("getAge");
method_getAge.setAccessible(true);
Object o = method_getAge.invoke(new MyPerson());

2.有参调用

Method method_setAge = cls.getMethod("setAge", Integer.TYPE); method_setAge.setAccessible(true);

method_setAge.invoke(person,122);

通过Method设置或者获取都一样,方法的调用通过invoke(),必须传入当前对象,后面有多少个参数就一一对应,没有就不传。

这样基本就完成了反射的作用

注意:

Method提供了getDefaultValue(),这个方法是获取注解提供的默认值,不是获取目标类的值。如果不是通过注解设置默认值,返回的是null。

1.7.内部类的获取

我们上面都是介绍单独类,有些人说如果我的类是类部类怎么获取?其实很简单

Class clsParent = Class.forName("com.example.mylibrary.ref.Parent");

Class clsChild = Class.forName(clsParent.getName() + "$Child");

path=父类+$+内部类名称

二、动态代理Proxy

掌握了基本的反射使用,那我们继续了解动态代理。我们经常听到Hook,钩子说法很悬乎,也很科幻,一些公司面试喜欢闻,你会Hook嘛?其实就这些。没有那么悬乎,剩下的就是Hook涉及到的复杂场景,任何复杂的代码都是从基础做起。

动态代理通过Proxy类完成

1、获取一个代理对象:newProxyInstance

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
    Objects.requireNonNull(h);
    Class<?> caller = System.getSecurityManager() == null ? null : Reflection.getCallerClass();
    Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
    return newProxyInstance(caller, cons, h);
}

参数介绍

1.1、loader:加载器

1.2、interfaces:接口类接口,代理是通过代理类的接口进行拦截,而不是直接修改方法体

1.3、InvocationHandler:拦截器

拦截器介绍:

public Object invoke(Object proxy, Method method, Object[] objects)

1、proxy:代理对象

2、method:代理对象调用的方法

3、objects:参数

小试牛刀:

public static void main(String[] args) throws Exception {
        final PersonInterface person = new Child("sun");


        Object proxy = Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                String name = method.getName();

                Log.log("Object o, Method method, Object[] objects----------");
                if (method.getName().equals("setName")) {
                    String item=(String) objects[0];
                    objects[0]="modify="+item;
                }

                return method.invoke(person, objects);

            }
        });

        if (proxy instanceof PersonInterface) {
            PersonInterface testPerson = (PersonInterface) proxy;
            testPerson.setName("proxy");
        }



        PersonInterface testPerson = (PersonInterface) proxy;
        testPerson.setName("proxy");

        Log.log(testPerson.getName());
    }

package com.example.mylibrary.ref;

import com.example.mylibrary.Log;

public class Child implements PersonInterface {
    String name = "";

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

    @Override
    public String getName() {

        return name;
    }

    @Override
    public void setName(String name) {

        this.name = name;
    }


    public void show() {
        Log.log("name=" + name);
    }
}

package com.example.mylibrary.ref;

public interface PersonInterface {
    public String getName();
    public void setName(String name);

}

 

 

DEBUG日志
​​​​​​

 

 这样,我们就完成了Hook了。记住,动态代理是拦截接口,而不是修改方法体

 

 

 

 

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

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

相关文章

夯实C++基础学习笔记

第一章 内存模型和编译链接 1. 掌握进程虚拟地址空间区域划分 编程语言产生&#xff1a;指令数据 exe 磁盘加载到内存&#xff0c;不可能直接加载到内存。 x86系统&#xff1a;linux系统会给当前进程分配一个 2^32 大小的空间 4G 它不存在&#xff0c;你却看得见&#xff0…

Bio-Helix丨Bio-Helix艾美捷Ponceaus S染色液说明书

Bio-Helix艾美捷Ponceaus S染色液是一种用于评估蛋白质印迹转移效率的现成膜染色剂。该染色剂适用于硝化纤维或PVDF膜上的快速可逆蛋白质染色。Ponceau S染色是可逆的&#xff0c;可以在0.1%NaOH中短时间培养去除。 图&#xff1a; Ponceau S溶液可用于评估硝化纤维和PVDF膜上的…

正点原子stm32F407学习笔记6——外部中断实验

一、GPIO与中断线的映射关系 GPIO 的管脚GPIOx.0 ~ GPIOx.15(xA,B,C,D,E&#xff0c;F,G,H,I)分别对应中断线 0~15。这样每个中断线对应了最多 9 个 IO 口&#xff0c;以线 0 为例&#xff1a;它对应了 GPIOA.0、GPIOB.0、GPIOC.0、GPIOD.0、GPIOE.0、GPIOF.0、GPIOG.0,GPIOH.…

springcloud7:服务注册与发现总结篇

eureka总结 问题1&#xff1a;为什么使用服务注册&#xff1f; 服务越来越多&#xff0c;负责存储和管理维护服务地址 问题2&#xff1a;如何通过名称访问地址&#xff1f; 即服务中心存储的为名称地址的键值对&#xff0c;服务注册中心会通过名称来返回访问地址&#xff08;ip…

设备树属性获取,通过键获取值的相关函数实验

1.int of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value) 功能&#xff1a;获取32位无符号整型的值 参数&#xff1a; np:节点结构体指针 propname:键名 index:索引号 out_value:获取到的值 返回值&#xff1a;成功…

代码随想录训练营第29天|LeetCode 491.递增子序列、46.全排列、47.全排列 II

参考 代码随想录 题目一&#xff1a;LeetCode 491.递增子序列 这个题同样涉及到去重&#xff0c;但是不能再使用子集II那题中的去重方法&#xff0c;在那个题中用下面的代码去重&#xff1a; if (i > 0 && nums[i] nums[i - 1] && !used[i - 1]){conti…

【机器学习笔记】吴恩达机器学习

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4e3;专栏定位&#xff1a;为学习吴恩达机器学习视频的同学提供的随堂笔记。 &#x1f4da;专栏简介&#xff1a;在这个专栏&#xff0c;我将整理吴恩达机器学习视频的所有内容的笔记&…

麒麟系统上使用linuxdeployqt 编译安装

linuxdeployqt 去除git校验可以编译处理 银河麒麟V4&#xff0c;V10&#xff0c;本篇以V10记录&#xff0c;参照上一篇可安装V4、V7、V10三个版本&#xff0c;麒麟V4系自带了Qt&#xff0c;麒麟V10没有自带Qt&#xff0c;需要自己编译搭建环境。 linuxdeployqt编译&#xff08…

GlobalWebsoket.js 封装配置分析

GlobalWebsoket.js 封装配置分析前言一、 封装好的 GlobalWebsoket.js 1. GlobalWebsoket.js 二、GlobalWebsoket.js 代码分析1.GlobalWebsoket.js import 分析2.GlobalWebsoket.js 整体分析3. initWebSoket()3. getWebsoket4. sendSocketMessage三、GlobalWebsoket.js 使用分…

[附源码]Python计算机毕业设计大学生社团管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

大数据下一代变革之必研究数据湖技术Hudi原理实战双管齐下-中

文章目录核心原理数据写写操作UPSERT写流程INSERT写流程INSERT OVERWRIT写流程Key 生成策略删除策略写流程归纳数据读集成Spark使用环境准备spark-shell使用启动插入数据查询数据更新数据时间旅行查询增量查询指定时间点查询删除数据覆盖数据spark-sql使用启动创建表插入数据时…

状态压缩dp整理

目录蒙德里安的梦想详细解释Code最短Hamilton路径详细解释Code蒙德里安的梦想 求把 NMNMNM 的棋盘分割成若干个 121212 的长方形&#xff0c;有多少种方案。 例如当 N2&#xff0c;M4N2&#xff0c;M4N2&#xff0c;M4 时&#xff0c;共有 555 种方案。当 N2&#xff0c;M3N2…

语音合成技术入门之Tacotron

语音合成TTS 学习李宏毅课程。 输入文字&#xff0c;输出语音。 端到端之前TTS 18世纪就有&#xff0c;能找到demo的是1939年VODER。 就像电子琴一样&#xff0c;用手控制发出不同声音。 到1960年&#xff0c;IBM计算机能合成出歌唱声。 波形拼接 过去最常用的商用语音合…

策略验证_指标买点分析技法_运用MACD确定最佳买点

写在前面&#xff1a; 1. 本文中提到的“股票策略校验工具”的具体使用操作请查看该博文&#xff1b; 2. 文中知识内容来自书籍《同花顺炒股软件从入门到精通》 3. 本系列文章是用来学习技法&#xff0c;文中所得内容都仅仅只是作为演示功能使用 目录 解说 策略代码 结果 解…

【node.js】第六章 初识express

目录 1. express简介 1.1 express的概念 1.2 express的作用 2. express的使用 2.1 使用express创建Web服务器 2.2 监听GET/POST请求 2.3 获取URL的请求参数 3. 托管静态资源 3.1 express.static 3.2 托管多个静态资源 3.3 挂载路径前缀 4. nodemon 1. express…

Docker镜像操作、容器操作、数据卷及挂载数据卷

目录 一、镜像操作 案例&#xff1a;从DockerHub中拉取一个nginx镜像并查看 案例&#xff1a;利用docker save将nginx镜像导出磁盘&#xff0c;然后再通过load加载回来 二、容器操作 案例&#xff1a;创建运行一个Nginx容器 案例&#xff1a;创建并进入redis容器&#xf…

随笔记录-看nacos源码

Import注解 Import注解可以导入一些配置类&#xff0c;也就是创建一些指定对象。 使用Import导入普通类 项目结构中&#xff0c;import-consumer和import-provider都是同层级的module&#xff0c;import-consumer的pom文件中有引用import-provider的依赖&#xff1b; import…

baby_web (攻防世界)

前言: 这篇文章还是是为了帮助一些 像我这样的菜鸟 找到简单的题解 题目描述 进入网址 解题工具: 一个有F12的键盘 问题解析: 题目说想想初始页面是哪个 一般都是index.php 然后如题分析即可 科普时间叒到 HTTP状态码 &#xff08;英语&#xff1a;HTTP Status Code…

从零开始操作系统-08:计时器

这一节主要主要是计时器。 所需要的文件在Github&#xff1a;https://github.com/yongkangluo/Ubuntu20.04OS/tree/main/Files/Lec7-ExternalInterrupt 计时器&#xff1a; 可编程间隔计时器&#xff1a;PIT&#xff08;Programmalbe Interval Timer&#xff09;8254 使用A…

Windows下文本生成图像AI画图尝鲜体验

工具库 transformers 的开源方 Hugging Face 发布了一个专注于 diffuser 模型的开源库&#xff0c;我们可以基于它&#xff0c;仅仅通过几行代码就开始生成自己的艺术作画。不过这个 diffuser 库是一个基础实现版本&#xff0c;训练和学习的数据也没有 OpenAI 的 DALL-E2、谷歌…