00-JAVA基础-反射机制

反射

什么是反射

在Java中,反射(Reflection)是Java语言的一个特性,它允许程序在运行时检查类、接口、字段和方法的信息。通过反射,Java代码能够动态地创建对象、调用方法、改变字段的值等,而无需在编译时知道这些类的详细信息。

Java反射主要提供了以下功能:

  1. 获取类的信息:通过反射,你可以获取一个类的Class对象,进而获取该类的名称、父类、实现的接口、声明的字段和方法等信息。
  2. 创建对象:使用反射,你可以在运行时动态地创建类的对象实例,而无需使用new关键字。
  3. 调用方法:通过反射,你可以获取类的方法信息,并动态地调用这些方法,包括私有方法。
  4. 访问和修改字段:反射允许你获取类的字段信息,并可以动态地读取和修改这些字段的值,包括私有字段。
  5. 注解处理:反射也常用于处理Java注解,读取注解信息并执行相应的逻辑。

反射的主要用途包括:

  1. 框架设计:许多Java框架(如Spring、Hibernate等)都大量使用了反射,以实现灵活的配置和扩展。
  2. 测试:在单元测试中,反射经常用于创建和操作测试对象。
  3. 插件机制:在需要支持插件的系统中,反射可以帮助系统在运行时加载和调用插件。

然而,反射也有一些缺点和注意事项:

  1. 性能开销:反射操作通常比直接操作慢,因为涉及了更多的动态解析和类型转换。
  2. 安全问题:通过反射,可以访问和修改类的私有成员,这可能导致安全问题。
  3. 代码可读性:过度使用反射可能导致代码难以理解和维护。

Class对象

Class对象是Java反射机制的核心,它封装了类的元数据(包含了关于类的结构信息,如类的名称、父类、实现的接口、字段、方法、注解等),并提供了在运行时检查和操作这些信息的能力。

每个类在JVM中都有且只有一个与之对应的Class对象,这个对象在类被加载到JVM时由类加载器创建。

案例
UserDo.java

package demo1;/*** 定义UserDo用于测试反射** @author Anna.* @date 2024/4/3 23:06*/
public class UserDo {private String name;private Integer age;public String sex;public UserDo() {}public UserDo(String name, Integer age, String sex) {this.name = name;this.age = age;this.sex = sex;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}/*** 定义一个私有方法** @param content* @return void* @author Anna.* @date 2024/4/3 23:09*/private void sayHello(String content){System.out.printf("%s说%s%n",this.name,content);}@Overridepublic String toString() {return "UserDo{" +"name='" + name + '\'' +", age=" + age +", sex='" + sex + '\'' +'}';}
}

测试代码:

package demo1;/*** 测试反射* 获取Class的三种方式** @author Anna.* @date 2024/4/3 23:10*/
public class ReflexDemo1 {public static void main(String[] args) throws ClassNotFoundException {// 通过.class获取Class<UserDo> userDoClass1 = UserDo.class;System.out.println(userDoClass1);   // class demo1.UserDo// 通过UserDo实例调用getClass()方法获取Class<? extends UserDo> userDoClass2 = new UserDo().getClass();System.out.println(userDoClass2 == userDoClass1); // true// 通过Class.forName()获取Class<UserDo> userDoClass3 = (Class<UserDo>) Class.forName("demo1.UserDo");System.out.println(userDoClass3 == userDoClass2);   // true}
}

执行结果

在这里插入图片描述

获取Class对象

  1. 使用.class语法:Class<?> clazz = String.class;
  2. 使用对象的getClass()方法:String s = “Hello”; Class<?> clazz = s.getClass();
  3. 使用Class.forName(包路径.类名)方法(需处理ClassNotFoundException):Class<?> clazz = Class.forName(“java.lang.String”);

Class对象的作用

  1. 类实例化:使用newInstance()方法或反射API创建类的新实例(从Java 9开始,newInstance()方法已被弃用,应使用getDeclaredConstructor().newInstance())。
  2. 反射操作:通过Class对象可以获取类的字段、方法、注解等信息,并可以动态地调用方法、访问和修改字段。
  3. 类型检查与转换:Class对象可以用于运行时类型检查(instanceof)和类型转换(cast)。
  4. 注解处理:通过Class对象可以读取类、方法、字段上的注解信息

泛型与Class对象

泛型在Java中引入了类型参数的概念,但在运行时,由于类型擦除(Type Erasure),泛型类型信息并不完全保留。因此,使用反射处理泛型时可能会遇到一些限制。不过,可以通过一些技巧(如使用类型令牌)来在运行时保留部分泛型类型信息。(通过反射处理注解逻辑时详述)

安全性与性能

使用反射进行类操作时需要注意安全性和性能问题。反射可以绕过编译时的类型检查,从而可能导致运行时错误或安全漏洞。此外,反射操作通常比直接操作慢,因为它们涉及更多的动态解析和类型转换。

反射常用API

类型接口描述
java.lang.Class类-反射的核心类,提供了获取类的元数据信息的方法。
-getClass()调用类或接口实例的方法以获取其Class对象。
-.class语法直接通过类名获取其Class对象。
-Class.forName()通过类的全限定名获取其Class对象。
-getMethods()获取类的所有公共方法。
-getDeclaredMethods()获取类声明的所有方法,包括私有方法
-getConstructors()获取类的所有公共构造方法
-getDeclaredConstructors()获取类声明的所有构造方法,包括私有构造方法
-getFields()获取类的所有公共字段
-getDeclaredFields()获取类声明的所有字段,包括私有字段
-getAnnotations()获取类上的所有注解
-isAnnotationPresent()检查类上是否存在某个特定的注解。
java.lang.reflect.Method类-用于表示类的方法。
-invoke(Object obj, Object… args)调用方法,第一个参数是方法所属的对象,后面的参数是方法的参数。调用私有方法(需要先设置为可访问,通过设置method.setAccessible(true);实现)
-getParameterTypes()获取方法的参数类型数组。
-getReturnType()获取方法的返回类型。
java.lang.reflect.Field类-用于表示类的字段。访问私有字段需要设置为可访问(通过设置field.setAccessible(true);实现)
-get(Object obj)获取字段的值,第一个参数是字段所属的对象。
-set(Object obj)设置字段的值,第一个参数是字段所属的对象。
-getType()获取字段的类型。
java.lang.reflect.Constructor类-用于表示类的构造方法。
-newInstance(Object… initargs)使用此Constructor对象表示的构造方法来创建该构造方法的声明类的新实例。从Java 9开始,newInstance()方法已被弃用,应使用getDeclaredConstructor().newInstance()
java.lang.reflect.Modifier类-用于获取和操作修饰符的工具类。
-isPublic(int modifiers)检查修饰符是否表示类、方法或字段是公共的。
-isPrivate(int modifiers)判断修饰符是否是private。

注意:

setAccessible(true) 可以用来访问私有字段和方法,但这样会破坏封装性,并且可能带来安全风险。

反射操作通常比直接访问慢,因为它们涉及到更多的动态解析和类型转换。

使用反射时应谨慎处理异常,特别是ClassNotFoundException、NoSuchMethodException、IllegalAccessException和InvocationTargetException等。

反射API提供了非常强大的功能,但使用时也需要考虑到其潜在的安全和性能影响。在实际应用中,应仔细权衡反射带来的灵活性和可能带来的问题。

反射案例

package demo2;import demo1.UserDo;import java.lang.reflect.Constructor;
import java.lang.reflect.Method;/*** 测试反射* 1 通过反射创建UserDo对象,* 2 调用默认构造器创建对象* 3 调用公有方法设置name* 4 调用公有方法获取私有字段name属性值* 5 调用私有方法sayHello** @author Anna.* @date 2024/4/3 23:19*/
public class ReflexDemo {public static void main(String[] args) throws Exception {// 通过Class.forName()获取ClassClass<?> clazz = Class.forName("demo1.UserDo");// 获取构造方法Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(String.class, Integer.class, String.class);// 调用newInstance()初始化UserDoUserDo userDo = (UserDo) declaredConstructor.newInstance("张三", 20, "男");System.out.printf("输出初始化后的UserDo对象:%s%n", userDo);// 调用调用公有方法设置nameMethod setNameMethod = clazz.getDeclaredMethod("setName", String.class);// 方法调用前需要先初始化 否则会抛出 Exception in thread "main" java.lang.IllegalArgumentException: object is not an instance of declaring class// 从Java 9开始,newInstance()方法已被弃用,应使用getDeclaredConstructor().newInstance()UserDo userDo1 = (UserDo) clazz.getDeclaredConstructor().newInstance();// 执行共育setNameMethod方法setNameMethod.invoke(userDo1, "李四"); 调用公有方法获取私有字段name属性值Method getNameMethod = clazz.getMethod("getName");System.out.printf("通过反射调用getName:%s%n", getNameMethod.invoke(userDo1));// 因为已经拿到UserDo的实例也可以通过实例调用getNameSystem.out.printf("通过实例调用getName:%s%n", userDo1.getName());// 通过反射调用私有方法sayHelloMethod sayHelloMethod = clazz.getDeclaredMethod("sayHello", String.class);// 调用私有方法需要设置为可访问 否则会抛出异常 Exception in thread "main" java.lang.IllegalAccessException: class demo2.ReflexDemo cannot access a member of class demo1.UserDo with modifiers "privatesayHelloMethod.setAccessible(true);sayHelloMethod.invoke(userDo1, "通过反射调用私有方法sayHello说了你好啊");}
}

执行结果

在这里插入图片描述

比较反射与对象实例调用方法对比

package demo3;import demo1.UserDo;import java.lang.reflect.Method;/*** 比较反射与对象实例调用方法对比** @author Anna.* @date 2024/4/3 23:54*/
public class ReflexDemo {public static void main(String[] args) throws Exception {test1();test2();test3();}/*** 通过UserDo调用方法** @author Anna.* @date 2024/4/3 23:54*/public static void test1(){UserDo userDo = new UserDo();long start = System.currentTimeMillis();for (long i = 0; i < 10000000000L; i++) {userDo.getName();}long end = System.currentTimeMillis();System.out.printf("通过UserDo调用getName()方法耗时(ms):%s%n", end - start);}/*** 通过反射调用getName方法,不设置跳过安全检查** @author Anna.* @date 2024/4/3 23:57*/public static void test2() throws Exception {// 获取ClassClass<?> clazz = Class.forName("demo1.UserDo");// 初始化对象UserDo userDo = (UserDo) clazz.getDeclaredConstructor().newInstance();// 获取setName()方法Method getNameNameMethod = clazz.getMethod("getName");long start = System.currentTimeMillis();for (long i = 0; i < 10000000000L; i++) {getNameNameMethod.invoke(userDo);}long end = System.currentTimeMillis();System.out.printf("通过反射调用getName方法,不设置跳过安全检查(ms):%s%n", end - start);}/*** 通过反射调用getName方法,设置跳过安全检查** @author Anna.* @date 2024/4/3 23:57*/public static void test3() throws Exception {// 获取ClassClass<?> clazz = Class.forName("demo1.UserDo");// 初始化对象UserDo userDo = (UserDo) clazz.getDeclaredConstructor().newInstance();// 获取setName()方法Method getNameNameMethod = clazz.getMethod("getName");// 设置跳过安全检查getNameNameMethod.setAccessible(true);long start = System.currentTimeMillis();for (long i = 0; i < 10000000000L; i++) {getNameNameMethod.invoke(userDo);}long end = System.currentTimeMillis();System.out.printf("通过反射调用getName方法,设置跳过安全检查(ms):%s%n", end - start);}
}

执行结果:

在这里插入图片描述

结论:
从执行结果可以看出,反射在性能方面是有所损耗的,但是设置setAccessible(true)可以适当提升反射效率。

反射操作泛型

泛型在Java中引入了类型参数的概念,但在运行时,由于类型擦除(Type Erasure),泛型类型信息并不完全保留。

为了通过单设操作这些类型以配合实际开发的需要,Java新增了ParameterizedType,GenericArrayType,TypeVariable,WildcardType几种类型来代表不能为归一到Class类中的类型但是又和原始类型起名的类型。

  • ParameterizedType :表示一种参数化的类型,比如Collection
  • GenericArrayType :表示一种元素类型是参数化类型或者类型变量的数组类型
  • TypeVariable :是各种类型变量的公共接口
  • WildcardType:代表一种通配符类型表达式,比如:?,? extends Number, ?super Integer
package demo4;import demo1.UserDo;import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;/*** 反射操作泛型** @author Anna.* @date 2024/4/4 0:48*/
public class ReflexDemo {public void test01(Map<String, UserDo> map, List<UserDo> userDoList) {System.out.println("调用test01");}public Map<String, UserDo> test02() {System.out.println("调用test02");return null;}public static void main(String[] args) throws Exception {// 获取制定法方法参数泛型信息Method test01Method = ReflexDemo.class.getMethod("test01", Map.class, List.class);// 获取方法的形式参数类型Type[] genericParameterTypes = test01Method.getGenericParameterTypes();for (Type genericParameter : genericParameterTypes) {// 判断形式参数类型是否为参数化的类型if (genericParameter instanceof ParameterizedType) {// 获取实际的类型参数Type[] actualTypeArguments = ((ParameterizedType) genericParameter).getActualTypeArguments();for (Type actualTypeArgument : actualTypeArguments) {System.out.printf("参数,泛型类型:%s%n", actualTypeArgument);}}}// 获取制定法方法返回值泛型信息Method test02Method = ReflexDemo.class.getMethod("test02");// 获取方法的形式参数类型Type genericReturnType = test02Method.getGenericReturnType();// 判断返回值 类型是否为参数化的类型if (genericReturnType instanceof ParameterizedType) {// 获取实际的类型参数Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();for (Type actualTypeArgument : actualTypeArguments) {System.out.printf("返回值,泛型类型:%s%n", actualTypeArgument);}}}
}

执行结果

在这里插入图片描述

gitee源码

git clone https://gitee.com/dchh/JavaStudyWorkSpaces.git

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

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

相关文章

debian的使用笔记

1. XP风格任务栏 安装 debian-live-12.5.0-amd64-xfce.iso 后&#xff0c;把下面的任务栏删除&#xff0c;把上面的任务栏移到下面&#xff0c;然后设置如下选项 2. 命令自动补全 sudo apt install bash-completion 3. 找不到命令 sudo apt install command-not-found sudo…

爬虫学习第一天

爬虫-1 爬虫学习第一天1、什么是爬虫2、爬虫的工作原理3、爬虫核心4、爬虫的合法性5、爬虫框架6、爬虫的挑战7、难点8、反爬手段8.1、Robots协议8.2、检查 User-Agent8.3、ip限制8.4、SESSION访问限制8.5、验证码8.6、数据动态加载8.7、数据加密-使用加密算法 9、用python学习爬…

计算机组成结构2

概念 存储系统 解决成本-速度-容量之前的矛盾问题 寄存器–cache–内存–硬盘–外存储 局部性原理 时间局部&#xff1a;相邻的时间访问同一个数据空间局部&#xff1a;相邻的空间地址会被连续访问 cache cpu与主存之间&#xff0c;命中cache后就不需要访问主存&#xff0c;…

C++相关概念和易错语法(3)(类的声明和定义、空指针分析、this指针)

1.类的声明和定义 注意类的声明和定义分离的时候&#xff0c;在定义处要使用域作用限定符&#xff0c;否则函数声明链接时的定位不到函数的定义。 这些成员变量、函数的作用于这个类域&#xff0c;将功能集成在一起&#xff0c;这体现出封装的思想。 在区分类的定义和声明时&…

Vue3_2024_7天【回顾上篇watch常见的后两种场景】___续

Vue3中监听多条数据的两种使用 1.watch【使用上一章写法&#xff0c;监听两个属性&#xff0c;然后执行相应操作…】 2.watchEffect【相对于使用watch&#xff0c;watchEffect默认页面初始加载&#xff0c;有点类似加配置&#xff1a;立即执行 immediate】 代码&#xff1a; …

这个故事有点长 - 东方绿舟

这个故事有点长 - 东方绿舟 这个地方很大&#xff0c;游玩一天是没有问题。东方绿舟的1号门入口处&#xff0c;是一个人工瀑布&#xff0c;上边写着东方绿舟几个大字。远远看去&#xff0c;这个瀑布非常壮观&#xff0c;水的流淌让人感到凉爽&#xff0c;很适合在那里拍照打卡。…

使用注意力机制的 LSTM 彻底改变时间序列预测

目录 一、说明二、LSTM 和注意力机制简介三、为什么要将 LSTM 与时间序列注意力相结合&#xff1f;四、模型架构训练与评估 五、验证六、计算指标七、结论 一、说明 在时间序列预测领域&#xff0c;对更准确、更高效的模型的追求始终存在。深度学习的应用为该领域的重大进步铺…

喜讯 ChatGPT 3.5 免登录|免注册就可以使用了

https://chat.openai.com/ 直接访问openai 官网直接使用&#xff0c;当然还是要魔法的&#xff0c;不用再去用别人二次开发的&#xff0c;还有次数限制&#xff0c;还有开会员&#x1f605;才能用的。&#x1f600;试用啦一下&#xff0c;基本秒回答&#xff0c;能力也是在线的…

【Python】无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称解决方案

【Python】无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称解决方案 大家好 我是寸铁&#x1f44a; 总结了一篇【Python】无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称解决方案✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 前言 今天寸铁…

渗透测试练习题解析 5(CTF web)

1、[安洵杯 2019]easy_serialize_php 1 考点&#xff1a;PHP 反序列化逃逸 变量覆盖 【代码审计】 通过 GET 的方式获取参数 f 的值&#xff0c;传递给变量 function 定义一个过滤函数&#xff0c;过滤掉特定字符&#xff08;用空字符替换&#xff09; 下面的代码其实没什么用…

【Java】jdk1.8 Java代理模式,Jdk动态代理讲解(非常详细,附带class文件)

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 一、什么是代理模式 想要学代理模式&#xff0c;我们就要先弄清一个概念“什么是代理”&#xff1f; 在我们的现实生活中&#xff0c;你或许不少听过关于代理的名词&#xff0c;如&#xff1a;代理商。那什么又叫做代理…

Rust---复合数据类型之结构体

目录 结构体的使用输出结果 结构体简化创建结构体更新语法元组结构体单元结构体&#xff08;unit struct&#xff09;结构体中的引用使用#[derive(Debug)]再次介绍 代码综合展示 与元组不同的是&#xff0c;结构体可以为内部的每个字段起一个富有含义的名称&#xff0c;因此无需…

【74LS191/48为可预置的四位二进制加/减法计数器3-9循环显示】2022-3-19

缘由我有电路原理图&#xff0c;想用proteus仿真&#xff0c;但是数码管不亮-嵌入式-CSDN问答 74LS191为可预置的四位二进制加/减法计数器 74ls191引脚功用 RCO进位/借位输出端 MAX/MIN进位/借位输出端 CTEN计数操控端 QA-QD计数输出端 U/D计数操控端 CLK时钟输入端 LO…

10-用PySpark建立第一个Spark RDD

目录 RDD概念RDD特点建立RDD的方式不同工具建立RDD的方式使用PySpark Shell(交互环境)建立RDD使用VSCode编程建立RDD使用Jupyter Notebook建立RDD 总结 PySpark实战笔记系列第一篇 RDD概念 Apache Spark的核心组件的基础是RDD。所谓的RDD&#xff0c;即弹性分布式数据集&#…

Linux 关闭防火墙命令(新手)

关闭防火墙 查看防火墙状态 systemctl status firewalld.service 临时关闭防火墙&#xff08;重启失效&#xff09; systemctl stop firewalld.service 永久关闭防火墙 systemctl disable firewalld.servicesudo systemctl enable firewalld&#xff0c;这种方式输入命令…

施耐德 Unity Pro PLC 编程软件介绍

Unity Pro 软件基本介绍 Unity Pro 是施耐德中大型 PLC 的编程软件&#xff08;<–> 对应西门子 Step7&#xff09; 支持的 PLC&#xff1a;施耐德中大型 PLC 中型 PLC&#xff1a;Premium、M340&#xff08;<–> 对应西门子 S7-300、S7-1200&#xff09;大型 PL…

【C++】探索C++中的类与对象(上)

​​ &#x1f331;博客主页&#xff1a;青竹雾色间. &#x1f618;博客制作不易欢迎各位&#x1f44d;点赞⭐收藏➕关注 ✨人生如寄&#xff0c;多忧何为 ✨ C是一种强大的编程语言&#xff0c;其面向对象的特性使得代码结构更加清晰、易于维护和扩展。在C中&#xff0c;类与…

Day83:服务攻防-开发组件安全JacksonFastJson各版本XStreamCVE环境复现

目录 J2EE-组件Jackson-本地demo&CVE 代码执行 (CVE-2020-8840) 代码执行 (CVE-2020-35728&#xff09; J2EE-组件FastJson-本地demo&CVE FastJson < 1.2.24 FastJson < 1.2.47 FastJson < 1.2.80 (利用条件比较苛刻) J2EE-组件XStream-靶场&CVE …

高度不同的流体瀑布css实现方法

商城商品列表 实现瀑布流展示&#xff0c;通过flex或grid实现会导致每行中的列高度一致&#xff0c;无法达到错落有致的感觉&#xff1b; 为此需要用到&#xff1a; CSS columns 属性 columns 属性是一个简写属性&#xff0c;用于设置列宽和列数。 CSS 语法 columns: column-wi…

字节新作:图像生成质量超越DiT

&#x1f31f;每日更新最新高质量论文&#xff0c;关注我&#xff0c;时刻关注最新大模型进展。&#x1f31f; &#x1f4cc; 元数据概览&#xff1a; 标题&#xff1a;Visual Autoregressive Modeling: Scalable Image Generation via Next-Scale Prediction作者&#xff1a…