Java 的反射机制你了解多少?
不知道多少次听说过了Java反射机制的使用,比如:Spring 框架如何实例化IoC容器中的Bean,编码过程中如何动态的清理对象中的字段信息等等。工作中只是听说、看同事们编码实践,但是自己却只是概念上的认识,浅显粗略,今天就补一下反射的知识点,自己欠下的债,迟早是要还的。
一. 什么是反射?
在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象方法的功能就称为java语言的反射机制。通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。
想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。
获取类的字节码文件对象有三种方式:
1 2 3 4 5 6 | Class clazz1 = Class.forName( "全限定类名" ); // 通过Class类中的静态方法forName,直接获取到一个类的字节码文件对象,此时该类还是源文件阶段,并没有变为字节码文件。 Class clazz2 = User. class ; // 当类被加载成.class文件时,此时Person类变成了.class,在获取该字节码文件对象,也就是获取自己, 该类处于字节码阶段。 User user = new User(); Class clazz3 = p.getClass(); // 通过类的实例获取该类的字节码文件对象,该类处于创建对象阶段 |
有了字节码文件对象才能获得类中所有的信息,我们在使用反射获取信息时,也要考虑使用上面哪种方式获取字节码对象合理,视不同情况而定。下面介绍Class类的功能。
二. 反射机制能够动态获取类的哪些信息?
2.1 通过字节码对象创建实例对象
// 通过字节码对象创建实例对象
Class<?> userClz1 = Class.forName("com.base.reflect.entity.User");
User user1 = (User) userClz1.newInstance();
log.info("userClz1 instance = {}", user1);
Class<?> userClz2 = User.class;
User user2 = (User) userClz1.newInstance();
log.info("userClz1 instance = {}", user2);
2.2 获取指定构造器方法,constructor 有参无参创建实例
1 2 3 4 5 6 7 8 9 | // 获取指定构造器,有参无参构造实例对象 Class<?> userClz3 = Class.forName( "com.base.reflect.entity.User" ); Constructor<?> constructor1 = userClz3.getConstructor(); User user3 = (User) constructor1.newInstance(); log.info( "constructor1 user3 = {}" , user3); Constructor<?> constructor2 = userClz3.getConstructor(String. class , String. class , String. class , Date. class , String. class , Date. class ); User user4 = (User) constructor2.newInstance( "admin" , "123456" , "admin" , new Date(), "admin" , new Date()); log.info( "constructor user4 = {}" , constructor2); |
总结上面创建实例对象:
a)Class类的 newInstance() 方法是使用该类无参的构造函数创建对象;
b)可以调用Class类的 getConstructor(Class<?>... parameterTypes) 方法获取一个指定参数的构造函数,然后再调用Constructor类的newInstance(value1, value2 ...)方法创建对象;
获取类中全部构造方法:
1 2 3 4 | // 获取当前类中 public 类型的构造函数 Constructor<?>[] constructors = userClz3.getConstructors();<br><br> // 获取当前类中 public protected default private 类型的构造函数 Constructor<?>[] declaredConstructors = userClz3.getDeclaredConstructors(); |
2.3 获取指定的方法,动态调用方法
1 2 3 4 5 6 7 8 | // 动态获取类中的方法 User user5 = new User( "lisi" , "123456" , "lisi" , new Date(), "lisi" , new Date()); log.info( "user5 = {}" , user5);<br> // 获取User类中的 setParams 方法,并指定方法中的参数类型 Method method = user5.getClass().getMethod( "setParams" , String. class , String. class );<br> // 执行方法,传递参数【指明运行哪个对象中的哪个方法】 method.invoke(user5, "hello" , "123123" ); log.info( "user5 = {}" , user5); |
输出结果:
1 2 3 4 | user5 = User(username=lisi, password= 123456 , createBy=lisi, createTime=Thu Oct 22 17 : 21 : 52 CST 2020 , updateBy=lisi, updateTime=Thu Oct 22 17 : 21 : 52 CST 2020 ) user5 = User(username=hello, password= 123123 , createBy=lisi, createTime=Thu Oct 22 17 : 21 : 52 CST 2020 , updateBy=lisi, updateTime=Thu Oct 22 17 : 21 : 52 CST 2020 ) |
总结上面动态调用类中的方法:
Class.getMethod(String, Class...) 和 Class.getDeclaredMethod(String, Class...) 方法可以获取类中的指定方法,如果为私有方法,则需要打开一个私有方法权限。
setAccessible(true);用 invoke(Object, Object...) 可以调用该方法;
获取类中全部方法:
1 2 3 4 | // 获取当前类中 public 类型的方法 Method[] methods = User. class .getMethods();<br> // 获取当前类中 public protected default private 类型的方法 Method[] declaredMethods = User. class .getDeclaredMethods(); |
2.4 获取指定的字段,动态设置对象中字段的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // 动态获取对象中的 Field User user6 = new User( "zhangsan" , "123456" , "zhangsan" , new Date(), "zhangsan" , new Date()); log.info( "user6 = {}" , user1); clearFields(user6, "createBy" , "createTime" , "updateBy" , "updateTime" ); log.info( "user6 = {}" , user6); <br><br> // 清理对象中的字段信息 private static void clearFields(User user1, String... params) throws IllegalAccessException, NoSuchFieldException { for (String param : params) { Field field = user1.getClass().getDeclaredField(param); field.setAccessible( true ); Object obj = field.get(user1); log.info( "obj = {}" , obj); field.set(user1, null ); } } |
输出结果:
1 2 3 4 5 | user6 = User(username=zhangsan, password= 123456 , createBy=zhangsan, createTime=Thu Oct 22 17 : 33 : 18 CST 2020 , updateBy=zhangsan, updateTime=Thu Oct 22 17 : 33 : 18 CST 2020 ) obj = zhangsan obj = Thu Oct 22 17 : 33 : 18 CST 2020 obj = zhangsan user6 = User(username=zhangsan, password= 123456 , createBy= null , createTime= null , updateBy= null , updateTime= null ) |
获取类中全部字段信息:
1 2 3 4 5 | // 获取当前类中 public 类型的字段 Field[] fields = User. class .getFields(); // 获取当前类中 public protected default private 类型的字段 Field[] fields = User. class .getDeclaredFields(); |
使用场景:在使用 MyBatis 拦截器 Interceptor 对应用添加、更新对象时,需要重新设置 createBy createTime updateBy updateTime 字段信息,动态的清理一些字段信息,然后重新设置。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】博客园携手 AI 驱动开发工具商 Chat2DB 推出联合终身会员
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记录一次线上服务OOM排查
· Linux实时系统Xenomai宕机问题的深度定位过程
· 记一次 .NET某汗液测试机系统 崩溃分析
· 深度解析Mamba与状态空间模型:一图带你轻松入门
· 记一次 .NET某电商医药网站 CPU爆高分析
· Bogus:.NET的假数据生成利器
· 如何做好软件架构师
· 记录一次线上服务OOM排查
· 阿里云IP遭受DDOS攻击 快速切换IP实践
· itextpdf 找出PDF中 文字的坐标