一文搞懂反射,还有谁不懂,直接甩给他

news2024/11/17 14:39:40

Hi,大家好,我是抢老婆酸奶的小肥仔。

反射,在我们日常开发中无时无刻,被大量运用在框架代码和工具代码中,反射可以通俗点讲就是一个类的自我剖析,通过反射可以获取到这个类所有信息,包括:属性,属性值,方法等等。

我们今天来初探下java中的反射,希望对大家有所帮助,开撸!

1、反射概念

Java反射机制:即在运行状态中,任意一个类,都能够知道这个类所有属性和方法;任意一个对象,都能够调用他的方法和属性。

1.1 反射流程

在java中,我们编写的类在编译时,会编译成.class文件,然后通过类加载器加载到JVM中,JVM会每个类创建一个Class对象,而Class对象中会保存每个的信息,例如:方法、属性等。JVM底层提供了一些native方法,让Java类获取到Class对象clz,进而获取到类的属性。

在上图中,可以获取类的对象有三种方式:

  1. 通过Object类中的getClass(),例如:实体先进行构造,然后调用getClass()。
  2. 通过对象的.class属性,即直接使用类名.class既可
  3. 通过Class.forName(),这是通过类名来获取对象,会有异常需要处理。

1.2 优缺点

优点:能够很方便的获取当前类的信息,能够创建灵活代码,更易于实现面向对象

缺点:1、反射需消耗一定的系统资源

2、破坏了封装性,可能会导致一些安全问题。

2、反射常用方法

在java.lang.reflect包下面,我们会看到定义的很多类。在日常开发中,大多数我们用不到,常用的也就是那么几个,如:Field(类的域/属性)、Method(类的方法)、Constructor(类的构造器)等,下面我们来看看这些类中定义的哪些方法。

准备工作:

创建两个实体类

/**
 * @author: jiangjs
 * @description:
 * @date: 2023/6/30 9:41
 **/
@Data
public class Animal {

    private String name;
    private Integer age;
    private String color;

    @Data
    public static class OtherAnimal{
        private String other;
    }
}

/**
 * @author: jiangjs
 * @description:
 * @date: 2023/6/30 9:51
 **/
@EqualsAndHashCode(callSuper = true)
@Data
public class Dog extends Animal{
    /**
     * 品种
     */
    private String breed;

    @Data
    public static class OtherDog{
        private String other;
    }
}

其中Dog继承了Animal,是Animal的子类。

2.1 Class类方法

方法名描述
forName(String className);获取当前类的对象
getClassLoader();获取当前类的加载器
getClasses();获取当前类及其父类中的所有公共类和接口的对象
getDeclaredClasses();获取当前类中所有类和接口类的对象
newInstance();创建类的实例
getName();获取当前类的完整路径名称
getSimpleName();获取类的名称
getPackage();获取当前类的包
getSuperclass();获取当前类的父类的对象
getInterfaces();获取当前类的实现类或接口对象

我们来看下代码实现:

public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        Class<Animal> clazz = Animal.class;
        Class<Dog> dogClazz = Dog.class;
        //获取类加载器
        ClassLoader classLoader = clazz.getClassLoader();
        log.info("类加载器:" + classLoader);
        //获取当前类及其父类中的所有公共类和接口的对象
        Class<?>[] classes = dogClazz.getClasses();
        for (Class<?> aClass : classes) {
            log.info("获取的类及父类中的类对象:" + aClass);
        }
        //获取当前类中所有类和接口类的对象
        Class<?>[] declaredClasses = dogClazz.getDeclaredClasses();
        for (Class<?> aClass : declaredClasses) {
            log.info("获取的类对象:" + aClass);
        }
        //创建类的实例
        Dog dog = dogClazz.newInstance();
        dog.setName("橙橙");
        log.info("创建类的实例:" + dog.getName());
        //获取当前类的完整路径名称
        String name = dogClazz.getName();
        log.info("获取当前类的完整路径名称:" + name);
        //获取类的名称
        String simpleName = dogClazz.getSimpleName();
        log.info("获取类的名称:" + simpleName);

        //获取当前类的包
        Package aPackage = dogClazz.getPackage();
        log.info("获取当前类的包:" + aPackage);
        //获取当前类的父类的对象
        Class<? super Dog> superclass = dogClazz.getSuperclass();
        log.info("获取当前类的父类的对象:" + superclass);
        //获取当前类的实现类或接口
        Class<?>[] interfaces = dogClazz.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            log.info("获取当前类的实现类或接口:" + anInterface);
        }
    }

输出:

2.2 Field属性方法

很多时候我们都是需要获取属性及属性值来做处理,特别是使用AOP时。

方法名描述
getField(String name);根据名称获取属性对象,属性值必须是公有的
getFields();获取该类所有公有的属性对象
getDeclaredField(String name);根据名称获取当前类的属性对象
getDeclaredFields();获得该类所有属性对象

代码实现:

//属性
        //根据名称获取属性对象
        Field breedField = dogClazz.getField("breed");
        log.info("根据名称获取属性对象:" + breedField);

        //获取该类所有公有的属性对象
        Field[] fields = dogClazz.getFields();
        for (Field field : fields) {
            log.info("获取该类公有的属性对象:" + field);
        }
        //获取当前类及父类指定的属性对象
        Field nameField = clazz.getDeclaredField("name");
        log.info("获取当前类及父类指定的属性对象:" + nameField);

        //获得该类所有属性对象
        Field[] clzFields = clazz.getDeclaredFields();
        for (Field field : clzFields) {
            log.info("获得该类所有属性对象:" + field);
        }

输出:

在属性对象中,也提供了一些方法:

  1. equals(Object obj) :属性与obj是否相等,相等则返回true,否则返回false。
  2. get(Object obj) :获取obj中对应的属性值。
  3. set(Object obj,Object value) :给obj中的属性赋值。

上述提供的属性方法中只能获取到当前类的属性,很多时候我们还需要知道当前类的父类有哪些属性。因此我们可以结合类中的getSuperclass()来获取。代码如下:

private static List<Field> getAllFields(Class<?> clazz) {
    List<Field> allFields = new ArrayList<>();
    while (Objects.nonNull(clazz)){
        Field[] declaredFields = clazz.getDeclaredFields();
        allFields.addAll(Arrays.asList(declaredFields));
        clazz = clazz.getSuperclass();
    }
    return allFields;
}

测试: 获取Dog实体及父类的属性。

List<Field> allFields = getAllFields(dogClazz);
for (Field field : allFields) {
    log.info("获取dog当前类及父类属性:" + field);
}

2.3 Method方法

方法名描述
getMethod(String name, Class…<?> parameterTypes)根据名称获取当前类及父类中的公有方法。name:方法名称,parameterTypes:参数类型,如String.class等,没有参数可以不填
getMethods();获取当前类及其父类的所有公有的方法
getDeclaredMethod(String name, Class…<?> parameterTypes)根据名称获取当前类的方法
getDeclaredMethods();获取当前类中所有的方法
getConstructor(Class…<?> parameterTypes)获取当前类的公有构造方法
getConstructors()获取当前类所有公有的构造方法
getDeclaredConstructor(Class…<?> parameterTypes)获取当前类及父类的所有构造方法
getDeclaredConstructors()获得当前类所有构造方法

代码实现:

 //method方法
Method setName = dogClazz.getMethod("setName",String.class);
log.info("获取setName方法:" + setName);


Method method = dogClazz.getDeclaredMethod("getBreed");
log.info("获取getBreed方法:" + method);
Method[] declaredMethods = dogClazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
     log.info("获取Dog所有方法:" + declaredMethod);
}

Constructor<Dog> constructor = dogClazz.getConstructor(String.class);
log.info("获取带参数构造方法:" + constructor);
Constructor<?>[] constructors = dogClazz.getConstructors();
for (Constructor<?> con : constructors) {
     log.info("获取Dog下所有构造方法:" + con);
}
Method[] methods = dogClazz.getMethods();
for (Method dogMethod : methods) {
    log.info("获取Dog及父类所有方法:" + dogMethod);
}

输出:

在Method方法中,可以使用invoke(Object obj, Object... args) 来指定obj对象中参数相同的方法。

2.4 Annotation注解方法

方法名描述
getAnnotation(Class annotationClass)获取当前类及其父类与参数类型匹配的公有注解对象
getAnnotations();获取当前类所有公有注解对象
getDeclaredAnnotation(Class annotationClass)获取当前类及其父类与参数类型匹配的所有注解对象
getDeclaredAnnotations()获取当前类中所有注解对象
isAnnotation()验证当前类是否为注解类型,是则返回true,否则返回false
isAnnotationPresent(Class<? extends Annotation> annotationClass)属性中指定类型是否为注解类型,是则返回true,否则返回false

代码实现:

 //Annotation注解
NotBlank annotation = dogClazz.getDeclaredField("breed").getAnnotation(NotBlank.class);
log.info("获取Dog中Data注解:" + annotation);
String message = annotation.message();
log.info("获取注解信息:" + message);
boolean isAnnotation = dogClazz.isAnnotation();
boolean notBlank = NotBlank.class.isAnnotation();
log.info("验证是否为注解类型的类:dog," + isAnnotation+ ";notBlank:" + notBlank);
boolean present = dogClazz.getDeclaredField("breed").isAnnotationPresent(NotBlank.class);
log.info("指定NotBlank是否为注解类型:" + present);

输出:

2.5 其他方法

除了上述我们常用的一些方法外,还有些不常用的,我们也整理下,以备不时之需吧。

方法名描述
isAnonymousClass()当前类是否为匿名类。是则返回true,否则返回false
isArray()是否为数组类型。是则返回true,否则返回false
isEnum()是否为枚举类型。是则返回true,否则返回false
isInstance(Object obj)obj是否该类的实例。是则返回true,否则返回false
isInterface()是否为接口。是则返回true,否则返回false
isMemberClass()是否为内部类
isLocalClass()是否为局部类

代码实现:

//其他
boolean anonymousClass = dogClazz.isAnonymousClass();
log.info("当前类是否为匿名类:" + anonymousClass);
boolean array = dogClazz.isArray();
log.info("是否为数据类型:" + array);
boolean isEnum = dogClazz.isEnum();
log.info("是否为枚举类型:" + isEnum);
boolean instance = dogClazz.isInstance(dog);
log.info("obj是否该类的实例:" + instance);
boolean anInterface = dogClazz.isInterface();
log.info("是否为接口:" + anInterface);
Class<?>[] clazzs = dogClazz.getDeclaredClasses();
for (Class<?> aClass : clazzs) {
    log.info("类名:"+aClass.getSimpleName() + ";是否为内部类:" + aClass.isMemberClass());
    log.info("类名:"+aClass.getSimpleName() + ";是否为局部类:" + aClass.isLocalClass());
}

输出:

上述就是我理解的反射及常用的方法。其实只需要多敲敲代码,很多东西都会变得熟练。

一言以蔽之:反射就是通过jdk提供的方法从JVM中获取保存的类的各种属性的操作。

谢谢大家听我唠叨。

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

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

相关文章

mysql数据库:read_me_recover_tn勒索恢复---惜分飞

最近有客户被MySQL删库勒索,现象如下:1. 删除掉以前的库,并创建一个同名库,并且会创建一个read_me_recover_tn库,类似下图: 2. 在read_me_recover_tn库中有一个readme表,每个被删除然后创建的库里面也有一个readme表 3. 每个readme表内容类似信息类似: mysql> desc read…

操作系统基础之磁盘

概述 基本概念 磁盘有正反两个盘面&#xff0c;每个盘面有多个同心圆&#xff0c;每个同心圆是一个磁道&#xff0c;每个同心圆又被划分为多个扇区&#xff0c;数据就被存在扇区中。 磁头首先寻找到对应磁道&#xff0c;然后等到磁盘进行周期旋转到指定的扇区&#xff0c;才…

Colab/PyTorch - 003 Transfer Learning For Image Classification

Colab/PyTorch - 003 Transfer Learning For Image Classification 1. 源由2. 迁移学习(ResNet50)2.1 数据集准备2.2 数据增强2.3 数据加载2.4 迁移学习2.5 数据集训练&验证2.6 模型推理 3. 总结4. 参考资料 1. 源由 迁移学习已经彻底改变了 PyTorch 中处理图像分类的方式…

shared_ptr 引用计数相关问题

前言 智能指针是 C11 增加的非常重要的特性&#xff0c;并且也是面试的高频考点&#xff0c;本文主要解释以下几个问题&#xff1a; 引用计数是怎么共享的、怎么解决并发问题的资源释放时&#xff0c;控制块的内存释放吗weak_ptr 怎么判断对象是否已经释放 文中源码用的是 L…

从零自制docker-12-【overlayfs】

文章目录 overlayfsexec.Command("tar", "-xvf", busyboxTarURL, "-C", busyboxURL).CombinedOutput()exec.Command格式差异 挂载mount卸载unmount代码地址结果演示 overlayfs 就是联合文件系统&#xff0c;将多个文件联合在一起成为一个统一的…

【VTKExamples::Rendering】第五期 环形阵列Rotations

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 公众号:VTK忠粉 前言 本文分享VTK样例环形阵列Rotations,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(^U^)ノ~YO 1. Rotations

程序环境和预处理、编译链接过程、编译的几个阶段、运行环境、预定义符号等的介绍

文章目录 前言一、程序的翻译环境和执行环境二、编译链接过程三、编译的几个阶段四、运行环境五、预定义符号总结 前言 程序环境和预处理、编译链接过程、编译的几个阶段、运行环境、预定义符号的介绍。 一、程序的翻译环境和执行环境 在 ANSI C 的任何一种实现中&#xff0c…

DDR5和LPDDR4/5 命令解析

关键名称介绍 DDR5 SDRAM和LPDDR4/5都采用了高级的命令集来支持更高效的内存管理和操作,其中“Multi-purpose command (MPC)”、“Mode Register Read (MRR)”、“Mode Register Write (MRW)”,以及“Write Pattern Command”是几种关键的命令类型,它们在内存初始化、配置和…

力扣 5-11

704. 二分查找 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 这道题目的前提是数组为有序数组&#xff0c;同时题目还强…

028.实现 strStr()

题意 给你两个字符串 haystack 和 needle &#xff0c;请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标&#xff08;下标从 0 开始&#xff09;。如果 needle 不是 haystack 的一部分&#xff0c;则返回 -1 。 难度 简单 示例 例 1 输入&#xff1a;hays…

Java----数组的定义和使用

1.数组的定义 在Java中&#xff0c;数组是一种相同数据类型的集合。数组在内存中是一段连续的空间。 2.数组的创建和初始化 2.1数组的创建 在Java中&#xff0c;数组创建的形式与C语言又所不同。 Java中数组创建的形式 T[] 数组名 new T[N]; 1.T表示数组存放的数据类型…

1290.二进制链表转整数

给你一个单链表的引用结点 head。链表中每个结点的值不是 0 就是 1。已知此链表是一个整数数字的二进制表示形式。 请你返回该链表所表示数字的 十进制值 。 示例 1&#xff1a; 输入&#xff1a;head [1,0,1] 输出&#xff1a;5 解释&#xff1a;二进制数 (101) 转化为十进制…

静态住宅代理 IP 的影响

在不断发展的在线业务和数字营销领域&#xff0c;保持领先地位势在必行。在业界掀起波澜的最新创新之一是静态住宅代理 IP 的利用。这些知识产权曾经是为精通技术的个人保留的利基工具&#xff0c;现在正在成为各行业企业的游戏规则改变者。 一、静态住宅代理IP到底是什么&…

LeetCode/NowCoder-链表经典算法OJ练习1

目录 说在前面 题目一&#xff1a;移除链表元素 题目二&#xff1a;反转链表 题目三&#xff1a;合并两个有序链表 题目四&#xff1a;链表的中间节点 SUMUP结尾 说在前面 dear朋友们大家好&#xff01;&#x1f496;&#x1f496;&#x1f496;数据结构的学习离不开刷题…

实现树莓派DS18B20读取温度(OneWire)

简介 使用的是树莓派3B, Go编程实现OneWire方式读取DS18B20温度。 接线 DS18B20 包含经典三线&#xff0c; VCC和GND自不必说&#xff0c; 主要的是DQ线&#xff0c; 需要接4.7K的上拉电阻&#xff0c; 即4.7K欧姆的电阻接到DQ和VCC&#xff0c; 否则树莓派识别不到DS18B20&am…

2024kali linux上安装java8

1 kali下载Java 8安装包 访问Oracle官网或其他可信的Java下载站点&#xff0c;如华为云的开源镜像站&#xff08;例如&#xff1a;https://repo.huaweicloud.com/java/jdk/8u202-b08/jdk-8u202-linux-x64.tar.gz&#xff09;。 确保下载的是与你的Kali Linux系统架构&#xf…

Covalent Network(CQT)通过 “新曙光” 计划实现重要里程碑,增强以太坊时光机,提供 30% 的年化质押收益率

Covalent Network&#xff08;CQT&#xff09;作为集成超过 280 条区块链&#xff0c;并服务于超过 2.8 亿个钱包的领先结构化数据基础设施层&#xff0c;宣布了其战略计划 “新曙光” 中的一个重要进展。随着网络升级并完成了准备工作的 75%&#xff0c;这将为即将部署的以太坊…

2024数维杯数学建模B题完整论文讲解(含每一问python代码+结果+可视化图)

大家好呀&#xff0c;从发布赛题一直到现在&#xff0c;总算完成了2024数维杯数学建模挑战赛生物质和煤共热解问题的研究完整的成品论文。 本论文可以保证原创&#xff0c;保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃圾半成品论文。 B题论…

基于单片机的直流电机检测与控制系统

摘要&#xff1a; 文章设计一款流电机控制系统&#xff0c;以 STC89C51 作为直流电机控制系统的主控制器&#xff0c;采用 LM293 做为驱动器实现 对直流电机的驱动&#xff0c;采用霍尔实现对直流电机速度的检测&#xff1b;本文对直流电机控制系统功能分析&#xff0c;选择确…

探索Linux:深入理解各种指令与用法

文章目录 cp指令mv指令cat指令more指令less指令head指令tail指令与时间相关的指令date指令 cal指令find指令grep指令zip/unzip指令总结 上一个Linux文章我们介绍了大部分指令&#xff0c;这节我们将继续介绍Linux的指令和用法。 cp指令 功能&#xff1a;复制文件或者目录 语法…