通俗易懂讲反射

可进入本人语雀文档看,格式更清晰明了哦
https://www.yuque.com/docs/share/3c013ec6-6c35-4854-aaf6-ff9a6e8a6af2?# 《通俗易懂讲反射》

文章架构

通俗易懂讲反射

反射是个啥

反射就是在运行期,可以获取任意一个对象的属性和方法,可以调用任意一个对象的方法和属性。是一种动态获取对象信息以及动态调用对象的方法。最常见的场景就是在动态代理。而动态代理应用最广的地方就是各种框架,比如:Spring

Class 对象

了解反射必定离不开 Class 对象。

爪洼国的人都知道,代码编写完后是要编译成 .class 文件的。这里的一个个 .class 文件,被虚拟机加载到内存后就是以 Class 对象存在,一个类只能有一个 .class 文件,因此,一个类也就对应一个 Class 对象,Class 对象是类的描述信息,比如:字段、方法、注释等等等等,只要你想得到的类结构,他在 Class 对象内都有体现,并且都能通过 Class 对象获取到。

如何获取 Class 对象引用

有以下两种方式获取:

  • 使用 Class 类的一个静态方法 forName() 获取:Class clazz = Class.forName(“className”);
  • 通过类字面常量获取:Class = String.class;

Class 类常用方法

这里偷个懒,进入 class 文件,通过 idea alt + 7 快捷键就可以展示类拥有的字段、方法。

通过方法名其实就大概能猜出这个方法是干嘛的了。

比如:

这里的例子可能有点绕,就随便拿一个类来举例:比如 String 类。

String 类有一个描述其结构的 Class 对象,下面用 Class 来表示。

  • newInstance():通过 Class 对象实例化一个 String 对象。
  • getName():获取 String 对象的类名
  • getInterfaces():获取 String 对象所实现的所有接口信息
  • getMethods():获取 String 对象所有的方法信息

通俗易懂讲反射

代理

既然反射应用最广的地方就是动态代理,那么只介绍如何在运行中获取一个类的结构就有点说不过去了。好歹也得介绍下动态代理吧。

动态代理

动态代理一共需要三个东西:

  • 接口,以下的代码是 Interface 接口
  • 实现接口的类,也是要被代理的类,以下的代码是 Clazz 类
  • 实现 InvocationHandler 接口的代理类,以下的代码是 ProxyHandler 类。

代理类就是通过现有的字节码,通过增强的方式对要调用的方法进行增强,最后动态生成新的字节码文件,并且生成新的 .class 文件,最后通过反射的方式执行增强的方法。

如下,ProxyHandler 类中的 invoke 方法就是增强后的方法,这里在原来的被代理类执行前和执行后分别打印了两行字符串,实际应用场景可能是事务的开启和提交。

在 DynamicProxyDemo 类的 main 方法里面,通过 Proxy 类的静态方法 newProxyInstance() 生成一个被增强后的代理类,newProxyInstance() 方法需要三个入参

  • 类加载器,随便找个已经被加载的类获取其类加载器即可
  • Class 数组,被代理类所实现的接口数组,由此可见,jdk 的动态代理必须实现接口
  • 实现 InvocationHandler 接口的代理类对象,必须把被代理的类传入代理类中

获取到增强后的代理类后,将其强转成被代理类对象所实现的接口,然后调用方法,就可以发现,除了原本的实现,增强的逻辑也执行了。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface Interface {
    void m1();
    String m2();
    String m3(String a);
}

class Clazz implements Interface {
    @Override
    public void m1() {
        System.out.println("Clazz 的 m1() 方法");
    }

    @Override
    public String m2() {
        System.out.println("Clazz 的 m2() 方法");
        return "";
    }

    @Override
    public String m3(String a) {
        System.out.println("Clazz 的 m3() 方法");
        return a;
    }
}

class ProxyHandler implements InvocationHandler {
    private Object obj;

    public ProxyHandler(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.printf("------ method:%s start proxy ------%n", method.getName());
        Object invoke = method.invoke(obj, args);
        System.out.printf("------ method:%s end proxy ------%n%n", method.getName());
        return invoke;
    }
}

public class DynamicProxyDemo {

    public static void main(String[] args) {
        Interface proxy = (Interface) Proxy.newProxyInstance(DynamicroxyDemo.class.getClassLoader(), new Class[]{Interface.class}, new ProxyHandler(new Clazz()));
        proxy.m1();
        proxy.m2();
        proxy.m3("Clazz 的 m3() 方法");
    }
}

运行结果:

静态代理

既然都讲了动态代理,那就来讲讲静态代理吧。

动态代理是在运行时动态生成字节码,然后执行增强方法。

静态代理也需要三个东西:

  • 接口
  • 实现接口的被代理类
  • 实现接口的代理类

其中被代理类实现接口后,实现了自己的代码逻辑。

代理类也实现了相同的接口,然后在对应的方法里调用被代理类的方法就行了,需要增强方法的话在调用方法前或者调用方法后编写增强代码即可,所以代理类需要持有一个被代理类的引用。代码十分简单!看下面的例子。

interface StaticProxyInterface {
    void m1();
    String m2();
    String m3(String a);
}

class StaticProxyClazz implements StaticProxyInterface {
    @Override
    public void m1() {
        System.out.println("Clazz 的 m1() 方法");
    }

    @Override
    public String m2() {
        return "Clazz 的 m2() 方法";
    }

    @Override
    public String m3(String a) {
        return a;
    }
}

class FinalProxyClazz implements StaticProxyInterface {

    private StaticProxyInterface obj;

    public FinalProxyClazz(StaticProxyInterface obj) {
        this.obj = obj;
    }

    @Override
    public void m1() {
        System.out.println("------ start proxy ----");
        obj.m1();
    }

    @Override
    public String m2() {
        System.out.println("------ start proxy ----");
        return obj.m2();
    }

    @Override
    public String m3(String a) {
        System.out.println("------ start proxy ----");
        return obj.m3(a);
    }
}

public class StaticProxyDemo {

    public static void main(String[] args) {
        FinalProxyClazz finalProxyClazz = new FinalProxyClazz( new StaticProxyClazz());
        finalProxyClazz.m1();
        System.out.println(finalProxyClazz.m2());
        System.out.println(finalProxyClazz.m3("Clazz 的 m3() 方法"));
    }
}

运行结果:

Clazz 的 m2() 方法

Original: https://www.cnblogs.com/cyrus-s/p/15443201.html
Author: 三木同学
Title: 通俗易懂讲反射

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/715179/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • redis启动报无权限

    设置一下SELINUX 关闭SElinux 查看selinux状态 [root@localhost ~]# getenforce Enforcing 表示启动 临时关闭 [root…

    技术杂谈 2023年7月25日
    089
  • 个人学习-Linux-IO多路复用

    [1]confirmwz博客:Epoll原理解析https://blog.csdn.net/armlinuxww/article/details/92803381; [2]hech…

    技术杂谈 2023年6月21日
    0103
  • XCTF pwn新手区解题记录

    一、前言 闲来无事,刷刷ctf题 二、题目: level0 1、下载好题目后,拖入到kali中去,用 file 和 checksec 查看一下,可以发现该程序是 64&#x…

    技术杂谈 2023年7月11日
    060
  • Maven实现多module打包成单个jar

    首先我们开发的项目一定会是一个父项目和多个子项目 1:我这里有多个子项目,多个项目都继承父项目2:start模块最终会依赖所有的模块,如果我们在打包前不对父项目进行先打包那么,我们…

    技术杂谈 2023年6月1日
    086
  • SpringBoot整合多点套路,少走点弯路~

    持续原创输出,点击上方蓝字关注我 前言 Spring Boot 版本 * 找到自动配置类 * 注意 @Conditionalxxx 注解 * 注意 EnableConfigurat…

    技术杂谈 2023年7月24日
    085
  • Six—pytorch学习—索引与切片

    pytorch学习(3) 索引与切片 普通索引 冒号索引(切片) index_select 选择特定索引 masked_select 选择符合条件的索引 take 索引 普通索引 …

    技术杂谈 2023年6月21日
    066
  • kettle插入邮件

    posted @2020-12-04 14:58 cheng_blog 阅读(60 ) 评论() 编辑 Original: https://www.cnblogs.com/chen…

    技术杂谈 2023年7月24日
    081
  • 解决Ubuntu 20.04下VS code无法使用中文输入法的问题

    技术背景 在Ubuntu 20.04下,如果从应用商城中直接下载VS code,有可能会导致无法使用中文输入法的问题,那么就只能从其他地方写了中文再复制过来,非常的麻烦。从一些文章…

    技术杂谈 2023年7月25日
    068
  • 架构设计之设计模式总结

    在实际项目开发中我们会经常使用到设计模式,设计模式是否能够正确、合理、灵活的运用到项目当中,是评判你开发能力的重要指标之一, 这一方面需要你打下牢固的编程基础,同时也需要积累大量的…

    技术杂谈 2023年7月25日
    090
  • 学习链表复盘中

    链表基础知识 链表的分类 链表是一种通过指针串联在一起的线性结构,主要分为单链表、双向链表和循环链表。 单链表 单链表中每一个节点是由两部分组成,一个是数据域、一个是指针域(存放指…

    技术杂谈 2023年7月25日
    097
  • 《软技能-代码之外的生存指南》读书笔记

    《软技能-代码之外的生存指南》读书笔记 写在前面 最近项目相对松了一些,想静下心来看一些书,买了些DDD的书,记得这本书也是程序员必读的书之一,就凑单也买了纸质的来看看~ 抄录一些…

    技术杂谈 2023年7月24日
    053
  • vim编辑器命令

    参考: https://blog.csdn.net/weixin_44191814/article/details/120091363 vim编辑器 ## Vim基本模式 【对文件…

    技术杂谈 2023年7月24日
    053
  • 深入剖析堆原理与堆排序

    堆的介绍 完全二叉树:完全二叉树是满二叉树去除最后N个节点之后得到的树((N \geq0, N \in N^*)) 大根堆:节点的父亲节点比自身节点大,比如根节点的值为(8),比其…

    技术杂谈 2023年7月23日
    075
  • source insight4.0最常用到的设置

    1、常用功能 1.1:全局查找 1.2:当前文件查找 1.3:高亮设置 1.4:配置字体以及其他 1.5:配置自动缩进 1.6:其他 1. 常用功能 全局查找 Ctl+/ 查找到的…

    技术杂谈 2023年6月21日
    073
  • 交叉编译rust-openssl crate

    转自:https://www.javaroad.cn/questions/121768 我在Debian机器上,我想为我的Raspberry Pi 2交叉编译一个项目 . 我已经设…

    技术杂谈 2023年6月1日
    0103
  • static final 还是 final static

    The Java Language Specification recommends listing modifiers in the following order: Annot…

    技术杂谈 2023年6月1日
    087
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球