Java基础之反射详解

作者:福建选手阿俊 时间:2022-06-16 12:25:11 

前言

反射是我们框架的灵魂,反射也是我们框架的一个底层基石,没有反射也就没有框架,如果我们学好了反射,对我们阅读框架底层是有很大班助的——阿俊。有些文章上来就讲反射,就会很懵逼,不知道是干啥的,所以我们就引出一些问题来看看为什么需要反射

一、一个需求引出反射

看下面的问题
根据配置文件reflection.properties指定信息,创建People对象并调用方法hi


classullpath= com.reflection.People
method=hi

思考:使用现有技术,能做吗?
我们可以来试一试
我们根据要求创建出两个类和一个配置文件

Java基础之反射详解

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对象得到类的结构。这个对象就像一面镜子, 透过这个镜子看到类的结构,所以,形象的称之为反射。

我们可以拿第一节的例子,画图来说明

Java基础之反射详解

四、反射相关类

接下来讲完反射机制原理,我们就来看一下反射机制可以做那些事情

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则表示反射的对象执行访问检查

Java基础之反射详解

代码第12行加入method.setAccessible(true);//反射的对象在使用时取消访问检查,提高反射的效率
代码效果演示


传统方法调用hi耗时:3
反射机制调用hi耗时:16

来源:https://blog.csdn.net/qq_44891295/article/details/116380217

标签:Java,反射
0
投稿

猜你喜欢

  • SpringBoot如何整合redis实现过期key监听事件

    2023-08-04 18:51:19
  • C#限速下载网络文件的方法实例

    2023-07-01 01:33:15
  • Kotlin入门教程之开发环境搭建

    2022-04-22 20:58:12
  • 一文带你搞懂Java定时器Timer的使用

    2022-09-08 01:18:16
  • c#多线程网络聊天程序代码分享(服务器端和客户端)

    2022-08-10 00:32:48
  • 深入浅析TomCat Session管理分析

    2022-12-16 02:44:16
  • Java实现合并多个PDF的示例代码

    2023-04-29 13:25:32
  • 浅谈springboot的三种启动方式

    2022-07-31 10:57:47
  • C#利用GDI绘制常见图形和文字

    2023-04-09 20:44:40
  • java计算工作时间除去节假日以及双休日

    2023-09-24 20:34:58
  • 并发编程之Java内存模型顺序一致性

    2023-04-11 08:12:25
  • C#集合遍历时删除和增加元素的方法

    2021-12-11 18:53:24
  • Mybatis自定义TypeHandler解决特殊类型转换问题详解

    2023-04-14 09:50:51
  • Android View事件机制 21问21答

    2022-10-14 04:27:10
  • Logback 使用TurboFilter实现日志级别等内容的动态修改操作

    2022-06-10 04:17:53
  • Spring jackson原理及基本使用方法详解

    2021-10-03 08:28:18
  • Java负载均衡算法实现之轮询和加权轮询

    2023-07-16 15:27:17
  • Java使用arthas修改日志级别详解

    2023-02-04 23:09:26
  • Android实现沉浸式状态栏功能

    2022-10-25 23:19:36
  • SpringBoot与Angular2的集成示例

    2021-09-02 22:24:55
  • asp之家 软件编程 m.aspxhome.com