Java基础之反射详解
作者:福建选手阿俊 发布时间:2022-06-16 12:25:11
前言
反射是我们框架的灵魂,反射也是我们框架的一个底层基石,没有反射也就没有框架,如果我们学好了反射,对我们阅读框架底层是有很大班助的——阿俊。有些文章上来就讲反射,就会很懵逼,不知道是干啥的,所以我们就引出一些问题来看看为什么需要反射
一、一个需求引出反射
看下面的问题
根据配置文件reflection.properties指定信息,创建People对象并调用方法hi
classullpath= com.reflection.People
method=hi
思考:使用现有技术,能做吗?
我们可以来试一试
我们根据要求创建出两个类和一个配置文件
People 类
public class People {
private String name="zlj";
public void hi(){
System.out.println("hi"+name);
}
}
classullpath= com.reflection.People
method=hi
在QuestionReflectionQuestion类中进行测试,我们学Java的时候可知,可以通过如下的方法创建People对象并调用方法hi
public class QuestionReflectionQuestion {
public static void main(String[] args) {
//传统方式
People people = new People();
people.hi();
}
}
那我们如何通过配置文件reflection.properties指定信息,创建People对象并调用方法hi呢?
注意:这样的需求在学习框架时特别多,即通过外部文件配置,在不修改源码情况下,来控制程序,也符合设计模式的ocp原则(开闭原则:不修改源码来扩展功能)
尝试1:Java中有一个Properties类,可以读写配置文件,我们可以用它进行尝试
public class QuestionReflectionQuestion {
public static void main(String[] args) throws IOException {
//我们尝试做一下-》明白反射
//Java中有一个Properties类,可以读写配置文件,我们可以用它进行尝试
Properties properties = new Properties();
properties.load(new FileInputStream("src\\reflection.properties"));
String classullpath = properties.get("classullpath").toString();//classullpath:com.reflection.People
String method = properties.get("method").toString();//method:hi
System.out.println("classullpath:"+classullpath);
System.out.println("method:"+method);
//那我们可以不可以直接使用new classullpath();来创建呢?这样显然不行的
}
}
结论:尝试失败,我们就可以使用反射机制来解决上述:通过配置文件reflection.properties指定信息,创建People对象并调用方法hi,下一节进行代码演示
二、反射入门
解决第一节问题的代码演示
分四个步骤:
1.加载类,返回class类型对象:cls
2.通过cls得到你的加载类com.reflection.People的对象实例
3.通过cls得到你加载的类com.reflection.People的method=hi的方法对象(即:在反射中,可以把方法视为对象(即:万物皆对象))
4.通过method调用方法:即通过方法对象来实现调用方法
public class QuestionReflectionQuestion {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Properties properties = new Properties();
properties.load(new FileInputStream("src\\reflection.properties"));
String classullpath = properties.get("classullpath").toString();//classullpath:com.reflection.People
String methodName = properties.get("method").toString();//method:hi
System.out.println("====使用反射机制解决=====");
//1:加载类,返回class类型对象:cls
Class cls = Class.forName(classullpath);
//2:通过cls得到你的加载类com.reflection.People的对象实例
Object o = cls.newInstance();
System.out.println("o的运行类型="+o.getClass());//运行类型
//3.通过cls得到你加载的类com.reflection.People的method=hi的方法对象
//即:在反射中,可以把方法视为对象(即:万物皆对象)
Method method = cls.getMethod(methodName);
//4.通过method调用方法:即通过方法对象来实现调用方法
method.invoke(o);//传统方法:对象.方法() 反射机制 方法.invoke(对象)
}
}
====使用反射机制解决=====
o的运行类型=class com.reflection.People
hizlj
我们先知道一下反射实现方式的步骤即可,一些类的介绍,我们后续会进行介绍
如果Java没有反射机制,Java就不是一种动态语言,而且我们所说的spring mybatis 框架就都不存在了,反射机制是框架的灵魂,也是底层机制的基石
反射机制的牛逼之处就在于:可以通过外部文件配置,在不修改源码的情况下,来控制程序(这就是开闭原则)
比如,我们在People类中新增cry方法,我们就不需要在QuestionReflectionQuestion类中新增 people.cry();这个方法,只需要在配置文件中reflection.properties进行如下修改method=cry即可。
====使用反射机制解决=====
o的运行类型=class com.reflection.People
cryzlj
三、反射原理图
前面我们对反射进行了一个快速入门,接下来,我们来介绍一下它的原理
反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到
加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象) ,这个对象包含了类的完整结构信息。通过这个Class对象得到类的结构。这个对象就像一面镜子, 透过这个镜子看到类的结构,所以,形象的称之为反射。
我们可以拿第一节的例子,画图来说明
四、反射相关类
接下来讲完反射机制原理,我们就来看一下反射机制可以做那些事情
1.在运行时判断任意一个对象所属的类
2.在运行时构造任意一个类的对象
3.在运行时得到任意一个类所具有的成员变量和方法
4.在运行时调用任意一个对象的成员变量和方法
5.生成 *
反射相关的主要类:
1.java.lang.Class:代表个类, Class对象表示某 个类加载后在堆中的对象
2.java.lang.reflect.Method:代表类的方法
3.java.lang.reflect.Field: 代表类的成员变量
4.java.lang.reflect.Constructor:代表类的构造方法
我们上面第二节介绍了Method演示,我们接下来进行Field和Constructor演示
People.java
public class People {
public String name="zlj";
public People(){
this.name="zzg";
}
public People(String name){
this.name=name;
}
public void hi(){
System.out.println("hi"+name);
}
public void cry(){
System.out.println("cry"+name);
}
}
QuestionReflectionQuestion.java
public class QuestionReflectionQuestion {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Properties properties = new Properties();
properties.load(new FileInputStream("src\\reflection.properties"));
String classullpath = properties.get("classullpath").toString();//classullpath:com.reflection.People
String methodName = properties.get("method").toString();//method:hi
String fieldName = properties.get("field").toString();
String constructorName = properties.get("constructor").toString();
System.out.println("====使用反射机制解决=====");
//1:加载类,返回class类型对象:cls
Class cls = Class.forName(classullpath);
//2:通过cls得到你的加载类com.reflection.People的对象实例
Object o = cls.newInstance();
System.out.println("o的运行类型="+o.getClass());//运行类型
//3.通过cls得到你加载的类com.reflection.Field 的field=name的字段
//即:在反射中,可以把方法视为对象(即:万物皆对象)
//getField不可以得到私有属性
Field field = cls.getField(fieldName);
//4.通过field 调用方法:即通过方法对象来实现调用方法
//因为对象赋了初始值;所以就是zzg,而不是zlj
System.out.println(field.get(o));
System.out.println("People类的字段name是否为zlj:"+field.equals("zlj"));
//构造器的3,4和Field 一样
Constructor constructors = cls.getConstructor();//()中可以指定构造器参数类型。默认返回无参构造器
System.out.println(constructors);
Constructor constructor = cls.getConstructor(String.class);//传入构造参数的类型对象
System.out.println(constructor);
}
}
reflection.properties
classullpath= com.reflection.People
method=cry
field=name
constructor=People
演示
====使用反射机制解决=====
o的运行类型=class com.reflection.People
People类的字段name是否为zlj:false
public com.reflection.People()
public com.reflection.People(java.lang.String)
五、反射调用优化
前面我们说了反射的原理和反射的作用,接下来我们就说一下它的优缺点
反射优点和缺点
优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活没有反射机制,框架技术就失去底层支撑。
缺点:使用反射基本是解释执行,对执行速度有影响。
基于缺点,我们可以进行反射调用优化。
我们先看一下传统方法调用和反射机制调用的耗时
public class ReflectionOptimize {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
ReflectionOptimize.m1();
ReflectionOptimize.m2();
}
//传统方法调用hi
public static void m1(){
People people = new People();
long start = System.currentTimeMillis();
for (int i=0;i<900000;i++){
people.hi();
}
long end = System.currentTimeMillis();
System.out.println("传统方法调用hi耗时:"+(end-start));
}
//反射机制调用hi
public static void m2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> cls = Class.forName("com.reflection.People");
Object o = cls.newInstance();
Method method = cls.getMethod("hi");
12
long start = System.currentTimeMillis();
for (int i=0;i<900000;i++){
method.invoke(o);
}
long end = System.currentTimeMillis();
System.out.println("反射机制调用hi耗时:"+(end-start));
}
}
传统方法调用hi耗时:4
反射机制调用hi耗时:19
结论:使用反射机制调用方法耗时更长
我们可以进行如下优化
反射调用优化关闭访问检查
1.Method和Field、 Constructor对象都有setAccessible()方法
2.setAccessible作用是启动和禁用访问安全检查的开关
3.参数值为true表示 反射的对象在使用时取消访问检查,提高反射的效率。参数值为false则表示反射的对象执行访问检查
代码第12行加入method.setAccessible(true);//反射的对象在使用时取消访问检查,提高反射的效率
代码效果演示
传统方法调用hi耗时:3
反射机制调用hi耗时:16
来源:https://blog.csdn.net/qq_44891295/article/details/116380217
猜你喜欢
- MultipartResolver和ServletFileUpload冲突如果同时使用了MultipartResolver 和Servlet
- 今天老师想让我帮忙把她们200多张寸照换成白底的,这些寸照里面多为蓝色底,红色底。用ps?不!用java!!对,我第一反应就是用java,到
- 1. 包装类的介绍针对八种基本数据类型定义相应的引用类型--包装类(封装类),有了类的热点后,就可以调用类中的方法2. 基本数据类型 --&
- 安装Jenkins提示:首先Jenkins安装方式有2中,一种是yum安装,另一种是使用war的方式进行安装(war就需要安装tomcat)
- java遍历json字符串,取得相应KV值时,各种麻烦,比如将json中的list取出来转为JSONArray,再将list中的object
- JPanel是面板组件,非顶层容器,一个界面只有可以有一个JFrame窗体组件,但可以有多个Jpanel面板,而JPanel上也可以使用Fl
- 运行远程主机上的shell脚本下面的例子是教给大家如何通过java程序,运行远程主机上的shell脚本。(我讲的不是一个黑客学习教程,而是使
- 在web应用中,常常会遇见点击某个链接会弹出一个新的窗口,或者是相互关联的web应用 ,这样要去操作新窗口中的元素,就需要主机切换到新窗口进
- 本文实例为大家分享了C++实现俄罗斯方块的具体代码,供大家参考,具体内容如下先是效果图:主菜单:游戏:设置:错误处理:代码:#include
- 前言一个说难不难,说简单竟看不出来是哪里问题的一个bug。是的 可能自己能力和经验尚浅无法识别,下面你们能否用火眼金睛一眼让bug原形毕露(
- java里有数字long来表示大的整数,如果两个数字的范围超过了long,要做加法算法怎么做呢?这个问题在面试中经常碰到,如果之前没有经历的
- 复合语句Java的复合语句是以整个区块为单位的语句,由{}以及{}内包含的内容组成对于复合语句来说,复合语句创建了一个局部变量的作用域,该作
- 1.引言在开发中,拖放是一种比较常见的手势操作,使用它能够让应用的交互更加地便捷和友好,本文将简要介绍如何为Android中的View添加拖
- 简介optional类是java8中引入的针对NPE问题的一种优美处理方式,源码作者也希望以此替代null。历史1965年,英国一位名为To
- 前言MyBatis-Plus 是基于 MyBatis 进行封装的一套优秀的持久层框架,它提供了丰富的便捷操作方法和强大的代码生成器,大大简化
- 1、LongAdder由来LongAdder类是JDK1.8新增的一个原子性操作类。AtomicLong通过CAS算法提供了非阻塞的原子性操
- 本文实例为大家分享了PropertyDescriptor反射调用set和get方法,供大家参考,具体内容如下第一段:package com.
- What内含一个或多个maven模块的SpringBoot项目称为SpringBoot多模块项目Why便于维护 将一个大的单体项目分成多个子
- 本文实例讲述了java实现单词搜索迷宫游戏。分享给大家供大家参考。具体分析如下:我们在杂志上,经常能够看到找单词的小游戏,在一个二维表格中,
- 1、使用JPA 的@Enumerated 注解 ,可以直接将Enum映射到数据库中。但是value的值只有两种方式选择,一种是使用枚举的序号