Java反射详细介绍
反射
目录介绍
-
1.反射概述
- 1.1 反射概述
- 1.2 获取class文件对象的三种方式
- 1.3 反射常用的方法介绍
- 1.4 反射的定义
- 1.5 反射的组成
- 1.6 反射的作用有哪些
-
2.反射的相关使用
- 2.1.1 通过反射获取无参构造方法并使用
- 2.1.2 通过反射获取带参构造方法并使用
- 2.1.3 通过反射获取私有构造方法并使用
- 2.1.4 通过反射获取成员变量并使用
- 2.1.5 通过反射获取无参无返回值成员方法并使用
- 2.1.6 通过反射获取带参无返回值成员方法并使用
- 2.1.7 通过反射获取带参带返回值成员方法并使用
- 2.1.8 通过反射获取无参带返回值成员方法并使用
-
3.相关知识点
- 3.1.1 设置setAccessible(true)暴力访问权限
- 3.1.2 获取Filed两个方法的区别
- 3.1.3 获取Field的类型
- 3.1.4 Method获取方法名,获取方法参数
- 3.1.5 Method方法的invoke()方法执行
关于链接
0.问题答疑
- 0.1 被反射的类是否一定需要无参构造方法?为什么?
- 0.2 反射的使用有什么优势和劣势?为什么说反射可以降低耦合?
- 0.3 反射比较损耗性能,为什么这样说?能否通过案例对比说明反射机制损耗性能……
- 0.4 反射是一种具有与类进行动态交互能力的一种机制,为什么要强调动态交互呢?
- 0.5 Java反射中的setAccessible()方法是否破坏了类的访问规则
-
0.2 反射的使用有什么优势和劣势?
- 射的初衷不是方便你去创建一个对象,而是让你在写代码的时候可以更加灵活,降低耦合,提高代码的自适应能力。
-
0.4 反射是一种具有与类进行动态交互能力的一种机制,为什么要强调动态交互呢
- 动态加载,也就是在运行的时候才会加载,而不是在编译的时候,在需要的时候才进行加载获取,或者说你可以在任何时候加载一个不存在的类到内存中,然后进行各种交互,或者获取一个没有公开的类的所有信息,换句话说,开发者可以随时随意的利用反射的这种机制动态进行一些特殊的事情。
1.反射概述
1.1 反射概述
- JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
- 对于任意一个对象,都能够调用它的任意一个方法和属性;
- 这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
- 要想解剖一个类,必须先要获取到该类的字节码文件对象。
- 而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象
1.2 获取class文件对象的三种方式
- 1.2.1 这三种方式为:
- a:Object类的getClass()方法
- b:静态属性class
- c:Class类中静态方法forName()
-
1.2.2 第一种方法【Object类的getClass()方法】
- 1.在内存中新建一个Person的实例,对象p对这个内存地址进行引用
- 2.对象p调用getClass()返回对象p所对应的Class对
- 3.调用newInstance()方法让Class对象在内存中创建对应的实例,并且让p2引用实例的内存地址
Person p = new Person(); Class<?> cls=p.getClass(); Person p2=(Person)cls.newInstance();
-
1.2.3 第二种方法【静态属性class】
- 1.获取指定类型的Class对象,这里是Person
- 2.调用newInstance()方法在让Class对象在内存中创建对应的实例,并且让p引用实例的内存地址
Class<?> cls=Person.Class(); Person p=(Person)cls.newInstance();
-
1.2.4 第三种方法【Class类中静态方法forName()】
- 1.通过JVM查找并加载指定的类(上面的代码指定加载了com.fanshe包中的Person类)
- 2.调用newInstance()方法让加载完的类在内存中创建对应的实例,并把实例赋值给p
- 注意:如果找不到时,它会抛出 ClassNotFoundException 这个异常,这个很好理解,因为如果查找的类没有在 JVM 中加载的话,自然要告诉开发者。
Class<?> cls=Class.forName("com.yc.Person"); //forName(包名.类名) Person p= (Person) cls.newInstance();
1.3 通过反射获取无参构造方法并使用
- A:获取所有构造方法
- public Constructor<?>[] getConstructors()
- public Constructor<?>[] getDeclaredConstructors()
- B:获取单个构造方法
- public Constructor getConstructor(Class<?>... parameterTypes)
- public Constructor getDeclaredConstructor(Class<?>... parameterTypes)
- 方法关键字
- getDeclareMethods() 获取所有的方法
- getReturnType() 获取方法的返回值类型
- getParameterTypes() 获取方法的传入参数类型
- getDeclareMethod("方法名,参数类型.class,....") 获得特定的方法
- 构造方法关键字
- getDeclaredConstructors() 获取所有的构造方法
- getDeclaredConstructors(参数类型.class,....) 获取特定的构造方法
- 成员变量
- getDeclaredFields 获取所有成员变量
- getDeclaredField(参数类型.class,....) 获取特定的成员变量
- 父类和父接口
- getSuperclass() 获取某类的父类
- getInterfaces() 获取某类实现的接口
1.4 反射的定义
- JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制
1.5 反射的组成
-
由于反射最终也必须有类参与,因此反射的组成一般有下面几个方面组成:
- 1.java.lang.Class.java:类对象;
- 2.java.lang.reflect.Constructor.java:类的构造器对象;
- 3.java.lang.reflect.Method.java:类的方法对象;
- 4.java.lang.reflect.Field.java:类的属性对象;
-
反射中类的加载过程
- 根据虚拟机的工作原理,一般情况下,类需要经过:加载->验证->准备->解析->初始化->使用->卸载这个过程,如果需要反射的类没有在内存中,那么首先会经过加载这个过程,并在在内存中生成一个class对象,有了这个class对象的引用,就可以发挥开发者的想象力,做自己想做的事情了。
1.6 反射的作用有哪些
-
前面只是说了反射是一种具有与Java类进行动态交互能力的一种机制,在Java和Android开发中,一般情况下下面几种场景会用到反射机制.
- 需要访问隐藏属性或者调用方法改变程序原来的逻辑,这个在开发中很常见的,由于一些原因,系统并没有开放一些接口出来,这个时候利用反射是一个有效的解决方法
- 自定义注解,注解就是在运行时利用反射机制来获取的。
- 在开发中动态加载类,比如在Android中的动态加载解决65k问题等等,模块化和插件化都离不开反射,离开了反射寸步难行。
2.反射的相关使用
2.1.4 通过反射获取成员变量[包含私有]并使用
clazz = Class.forName("com.ycbjie.ycpaidian.four.ReflexUtils"); Constructor constructor = clazz.getDeclaredConstructor(Context.class); //设置安全检查,访问私有构造函数必须 constructor.setAccessible(true); Object[] obj = new Object[]{mContext}; //创建实例 ReflexUtils clazzObj = (ReflexUtils) constructor.newInstance(obj); //反射修改私有变量 // 获取声明的 code 字段,这里要注意 getField 和 getDeclaredField 的区别 Field gradeField = clazz.getDeclaredField("code"); // 如果是 private 或者 package 权限的,一定要赋予其访问权限 gradeField.setAccessible(true); // 修改 student 对象中的 Grade 字段值 gradeField.set(clazzObj, 2); LogUtils.e("点击3----"+clazzObj.getCode());
2.1.5 通过反射获取无参无返回值成员方法[包含私有]并使用
clazz = Class.forName("com.ycbjie.ycpaidian.four.ReflexUtils"); Constructor constructor = clazz.getDeclaredConstructor(Context.class); //设置安全检查,访问私有构造函数必须 constructor.setAccessible(true); Object[] obj = new Object[]{mContext}; //创建实例 ReflexUtils clazzObj = (ReflexUtils) constructor.newInstance(obj); // 获取私有方法,同样注意 getMethod 和 getDeclaredMethod 的区别 Method goMethod = clazz.getDeclaredMethod("getMethod"); // 赋予访问权限 goMethod.setAccessible(true); // 调用 goToSchool 方法。 goMethod.invoke(clazzObj);
2.1.6 通过反射获取带参无返回值成员方法并使用
clazz = Class.forName("com.ycbjie.ycpaidian.four.ReflexUtils"); Constructor constructor = clazz.getDeclaredConstructor(Context.class); //设置安全检查,访问私有构造函数必须 constructor.setAccessible(true); Object[] obj = new Object[]{mContext}; //创建实例 ReflexUtils clazzObj = (ReflexUtils) constructor.newInstance(obj); //这种不行,注意getDeclaredMethod和invoke方法需要传递参数 /*Method copyText = clazz.getDeclaredMethod("copyText"); copyText.setAccessible(true); copyText.invoke(clazzObj,"测试复制这个功能");*/ // 获取私有方法,同样注意 getMethod 和 getDeclaredMethod 的区别 Method copyText = clazz.getDeclaredMethod("copyText",String.class); // 赋予访问权限 copyText.setAccessible(true); // 调用 copyText 方法。 copyText.invoke(clazzObj,"测试复制这个功能");
2.1.7 通过反射获取带参带返回值成员方法并使用
clazz = Class.forName("com.ycbjie.ycpaidian.four.ReflexUtils"); Constructor constructor = clazz.getDeclaredConstructor(Context.class); //设置安全检查,访问私有构造函数必须 constructor.setAccessible(true); Object[] obj = new Object[]{mContext}; //创建实例 ReflexUtils clazzObj = (ReflexUtils) constructor.newInstance(obj); //这种不行,注意getDeclaredMethod和invoke方法需要传递参数 /*Method copyText = clazz.getDeclaredMethod("copyText"); copyText.setAccessible(true); copyText.invoke(clazzObj,"测试复制这个功能");*/ // 获取私有方法,同样注意 getMethod 和 getDeclaredMethod 的区别 Method copyText1 = clazz.getDeclaredMethod("copyText",String.class,String.class); // 赋予访问权限 copyText1.setAccessible(true); // 调用 copyText 方法 boolean isSuccess = (boolean) copyText1.invoke(clazzObj,"测试复制这个功能","1111");
3.相关知识点
3.1.1 设置.setAccessible(true)暴力访问权限
- 一般情况下,我们并不能对类的私有字段进行操作,利用反射也不例外,但有的时候,例如要序列化的时候,我们又必须有能力去处理这些字段,这时候,我们就需要调用AccessibleObject上的setAccessible()方法来允许这种访问,而由于反射类中的Field,Method和Constructor继承自AccessibleObject,因此,通过在这些类上调用setAccessible()方法,我们可以实现对这些字段的操作。
Field gradeField = clazz.getDeclaredField("code"); // 如果是 private 或者 package 权限的,一定要赋予其访问权限 gradeField.setAccessible(true); Method goMethod = clazz.getDeclaredMethod("getMethod"); // 赋予访问权限 goMethod.setAccessible(true);
3.1.2 获取Filed两个方法的区别
- 两者的区别就是 getDeclaredField() 获取的是 Class 中被 private 修饰的属性。 getField() 方法获取的是非私有属性,并且 getField() 在当前 Class 获取不到时会向祖先类获取。
//获取所有的属性,但不包括从父类继承下来的属性 public Field[] getDeclaredFields() throws SecurityException {} //获取自身的所有的 public 属性,包括从父类继承下来的。 public Field[] getFields() throws SecurityException {}
3.1.3 获取Field的类型
- 可以看到 getGenericType() 确实把泛型都打印出来了,它比 getType() 返回的内容更详细。
public Type getGenericType() {} public Class<?> getType() {} Field[] filed2 = clazz.getFields(); for ( Field f : filed2 ) { System.out.println("Field :"+f.getName()); System.out.println("Field type:"+f.getType()); System.out.println("Field generic type:"+f.getGenericType()); System.out.println("-------------------"); } //打印值 07-31 17:20:41.027 8700-8700/com.ycbjie.ycpaidian I/System.out: Field :cars 07-31 17:20:41.027 8700-8700/com.ycbjie.ycpaidian I/System.out: Field type:interface java.util.List 07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field generic type:java.util.List<com.ycbjie.ycpaidian.four.Car> 07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field :map 07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field type:class java.util.HashMap 07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field generic type:java.util.HashMap<java.lang.Integer, java.lang.String> 07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field :name 07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field type:class java.lang.String 07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field generic type:class java.lang.String
3.1.4 Method获取方法名,获取方法参数
//获取方法名 //获取方法参数 //返回的是一个 Parameter 数组,在反射中 Parameter 对象就是用来映射方法中的参数。 public Parameter[] getParameters() {} //Method获取方法名 Method[] declaredMethods1 = clazz.getDeclaredMethods(); for ( Method m : declaredMethods1 ) { System.out.println("method name:"+m.getName()); } //获取方法参数 for ( Method m : declaredMethods1 ) { System.out.println("获取方法参数method name:"+m.getName()); //获取参数 Parameter[] paras; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { paras = m.getParameters(); for (Parameter c : paras ) { System.out.println("获取参数parameter :"+c.getName()+" "+c.getType().getName()); } } //获取所有的参数类型 Class[] pTypes = m.getParameterTypes(); for ( Class c : pTypes ) { System.out.print("参数类型method para types:"+ c.getName()); } System.out.println(); System.out.println("=========================================="); } //打印日志如下所示: 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: ========================================== 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取方法参数method name:copyText 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取参数parameter :arg0 java.lang.String 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 参数类型method para types:java.lang.String 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: ========================================== 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取方法参数method name:copyText 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取参数parameter :arg0 java.lang.String 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取参数parameter :arg1 java.lang.String 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 参数类型method para types:java.lang.String参数类型method para types:java.lang.String 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: ========================================== 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取方法参数method name:getCode 07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: ========================================== 07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取方法参数method name:getUserInfo 07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取参数parameter :arg0 java.lang.String 07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 参数类型method para types:java.lang.String 07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: ========================================== 07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取方法参数method name:setCode 07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取参数parameter :arg0 int 07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 参数类型method para types:int 07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: ==========================================
3.1.5 Method方法的invoke()方法执行
-
Method 调用 invoke() 的时候,存在许多细节:
- invoke() 方法中第一个参数 Object 实质上是 Method 所依附的 Class 对应的类的实例,如果这个方法是一个静态方法,那么 ojb 为 null,后面的可变参数 Object 对应的自然就是参数。
- invoke() 返回的对象是 Object,所以实际上执行的时候要进行强制转换。
- 在对Method调用invoke()的时候,如果方法本身会抛出异常,那么这个异常就会经过包装,由Method统一抛InvocationTargetException。而通过InvocationTargetException.getCause() 可以获取真正的异常。
关于我的博客
- 我的个人站点:www.yczbj.org,www.ycbjie.cn
- github:https://github.com/yangchong211
- 知乎:https://www.zhihu.com/people/yang-chong-69-24/pins/posts
- 简书:http://www.jianshu.com/u/b7b2c6ed9284
- csdn:http://my.csdn.net/m0_37700275
- 喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
- 开源中国:https://my.oschina.net/zbj1618/blog
- 泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
- 邮箱:yangchong211@163.com
- 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
- segmentfault头条:https://segmentfault.com/u/xiangjianyu/articles
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Java生产填坑经历之全面回忆
屏幕快照 2018-07-30 下午4.01.49.png 生产填坑的经历为什么要称之为全面回忆呢,因为恰巧笔者之前看了一部科幻电影,名字就叫《全面回忆》 Total Recall,该片于2012年10月20日在中国上映,豆瓣评分7.0。 全面回忆的剧情简介...... 欢迎来到Rekall,它是一个能够把你的梦境变成现实的工厂。主人公道格拉斯·奎德(柯林·法瑞尔 Colin Farrell 饰)是一名普通的工厂工人。尽管他有一位漂亮的妻子(凯特·贝金赛尔 Kate Beckinsale 饰),两人看似恩爱,但他内心似乎依然不满足。思绪旅行听起来像是个完美的假期,让他从沮丧的生活中得到放松——作为一名特工的真实记忆可能正是他所需要的。但是当这个过程出现可怕的异常时,奎德变成了一个被追杀的逃犯。他发现自己正在躲避受考哈根长官(布莱恩·科兰斯顿 Bryan Cranston 饰)控制的警察,没有一个人是奎德可以信任的,除了一名叛军的女战士(杰西卡·贝尔 Jessica Biel 饰),她为地下抵抗组织的首领(比尔·奈伊 Bill Nighy 饰)工作。幻想和现实之间的界线变得模糊,他的命运...
- 下一篇
《码农翻身》之Java帝国
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a724888/article/details/81321455 微信公众号【Java技术江湖】一位阿里 Java 工程师的技术小站。(关注公众号后回复”Java“即可领取 Java基础、进阶、项目和架构师等免费学习资料,更有数据库、分布式、微服务等热门技术学习视频,内容丰富,兼顾原理和实践,另外也将赠送作者原创的Java学习指南、Java程序员面试指南等干货资源) 《码农翻身》读书笔记之Java帝国 Java帝国(Java语言发展,JVM和class,持久化和IO,JDBC与DP,JTA与分布式事务,JSP与servlet等等内容) 这是我的后端读书笔记系列文章的第二篇,选取的是最近刚刚圈粉的知名博主刘欣创作的《码农翻身》。这篇文章只是第一部分。 本文内容主要根据知名博主刘欣一作《码农翻身》的内容总结而来,本书的内容风趣幽默,讲解计算机理论原理也是十分透彻,由于书中常常以小故事的形式出现,为了方便学习和回顾,我把它们进行了一些改编和整理,便于自己和跟多人阅读。 本篇文章主要讲述的书中...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Linux系统CentOS6、CentOS7手动修改IP地址
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS6,CentOS7官方镜像安装Oracle11G
- 设置Eclipse缩进为4个空格,增强代码规范
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS7,CentOS8安装Elasticsearch6.8.6
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作