java中反射有什么作用?

2023-05-16

前言

反射blog有很多,不再赘述,但是反射的作用具体实现场景就会比较少,这里举个例子

一个需求

使用参数的方式传入需要执行的类名,然后执行相应类的同名方法

普通的实现方法(静态加载)

因为需要考虑执行的是不同类的同名方法,所以用接口来规范这个方法,然后增加两个类去实现这个接口即可,最后通过判断执行哪一个类

  • 接口 Stand
package com.test.dynamicLoading;

public interface Stand {
    public void run();

}
  • 实现类A.java
package com.test.dynamicLoading;

public class A implements Stand{
    public void run() {

        System.out.println("A running");
    }
}

  • 实现类B.java
package com.test.dynamicLoading;

public class B implements Stand{

    public void run() {
        System.out.println("B running");
    }

    private void privateRun() {
        System.out.println("C privateRun running");
    }
}
  • 执行类 runAll.java
package com.test.dynamicLoading;

public class runAll {

    public static void main(String[] args) {
        if ("A".equals(args[0])) {
            System.out.println("loading A");
            A a = new A();
            a.run();
        }
        if ("B".equals(args[0])) {
            System.out.println("loading B");
            B b = new B();
            b.run();
        }
    }

}

在命令行进行编译

~/Desktop/sourceCode/java-test/src/main/java/com/test/dynamicLoading> javac runAll.java

runAll.java:8: 错误: 找不到符号
            A a = new A();
            ^
  符号:   类 A
  位置: 类 runAll
runAll.java:8: 错误: 找不到符号
            A a = new A();
                      ^
  符号:   类 A
  位置: 类 runAll
runAll.java:13: 错误: 找不到符号
            B b = new B();
            ^
  符号:   类 B
  位置: 类 runAll
runAll.java:13: 错误: 找不到符号
            B b = new B();
                      ^
  符号:   类 B
  位置: 类 runAll
4 个错误
exit 1

Ok,符合预期,对于可能使用到的类,需要同时编译,这是静态加载(静态加载的类的源程序在编译时期加载(必须存在))的过程,也就是说,编译runAll.java的时候,就要求所有可能被用到的类都需要存在且一起编译

~/Desktop/sourceCode/java-test/src/main/java/com/test/dynamicLoading> javac runAll.java B.java A.java Stand.java

以上编译通过,在同级目录会产生runAll.class,A.class,B.class文件,开始执行

~/Desktop/sourceCode/java-test/src/main/java> java com.test.dynamicLoading.runAll A

loading A
A running
~/Desktop/sourceCode/java-test/src/main/java> java com.test.dynamicLoading.runAll B

loading B
B running

需要注意的是,执行java的时候,因为有package的关系,所以需要退出到响应的目录层级操作:至于为什么请参考:使用java命令运行class文件提示“错误:找不到或无法加载主类“的问题分析

程序顺利执行,对应传入的不同参数,执行不同的效果,好了重头戏来了,如果我现在要增加一个类C,然后接着执行呢?如何操作

  • 第一步:先写类C.java
package com.test.dynamicLoading;

public class C implements Stand{
    @Override
    public void run() {
        System.out.println("C running");
    }
}

第二步,修改runAll.java代码

package com.test.dynamicLoading;

public class runAll {
		
    public static void main(String[] args) {
        // 静态加载类,在编译时刻就需要加载所有的可能使用到的类
        if ("A".equals(args[0])) {
            System.out.println("loading A");
            A a = new A();
            a.run();
        }
        if ("B".equals(args[0])) {
            System.out.println("loading B");
            B b = new B();
            b.run();
        }
       // 新增C
        if ("C".equals(args[0])) {
            System.out.println("loading C");
            C c = new C();
            c.run();
        }
    }

}

第三步,重新编译

~/Desktop/sourceCode/java-test/src/main/java/com/test/dynamicLoading> javac runAll.java C.java Stand.java A.java B.java

因为要重新编译runAll.java,所以需要把依赖的几个类包括接口都同时编译

第四步,执行

~/Desktop/sourceCode/java-test/src/main/java> java com.test.dynamicLoading.runAll C

loading C
C running

目前为止都是正常的,但是,这样操作巧妙么?不,很臃肿,而且随着类的增加,需要不断的去变更runAll.java代码,而且需要重新进行编译才能把类加载进去

使用反射的方式进行动态加载

反射及作用是什么这里不再赘述,有几篇文章很好可以建立认知

  1. 谈谈Java反射机制:https://www.jianshu.com/p/6277c1f9f48d
  2. 框架开发之Java注解的妙用:https://www.jianshu.com/p/b560b30726d4
  3. 深入理解Java类型信息(Class对象)与反射机制:https://blog.csdn.net/javazejian/article/details/70768369
  4. 我竟然不再抗拒 Java 的类加载机制了:https://zhuanlan.zhihu.com/p/73078336
  5. 好怕怕的类加载器:https://zhuanlan.zhihu.com/p/54693308

步骤一:写基础类A.java,B.java,Stand.java,上文中都可以直接使用

步骤二:重新写一个使用反射操作来动态获取的执行类的类

package com.test.dynamicLoading;
import java.lang.reflect.Method;

public class dynamicRunAll {

    public static void main(String[] args) {
         try {
             Class c1 = Class.forName(args[0]); 
             System.out.println("获取该类的名称:" + c1.getName());
             Method[] methods = c1.getDeclaredMethods();  // 获取申明的方法,包括private修饰的
             for(Method m:methods) {
                 System.out.println("该类的方法有:" + m);
             }
             Stand stand  = (Stand)c1.newInstance();
             stand.run();
         } catch (Exception e) {
            System.out.println(e);
        }
    }
}

第三步:编译

# 先删除所有的.class文件
~/Desktop/sourceCode/java-test/src/main/java/com/test/dynamicLoading> rm *.class  
# 编译文件
~/Desktop/sourceCode/java-test/src/main/java/com/test/dynamicLoading> javac dynamicRunAll.java Stand.java

这边需要注意的是,因为动态加载的关系,所以编译时并不需要提前知道该类(可以实现在运行过程中,通过传入不同的"className"创建不同的实例,这个过程完全可以在运行时确定),大家都遵循着一个规范,只要这个规范下的类都可以被之后加载,用来约束的就是Stand这个接口

上述过程结束后,我们得到了dynamicRunAll.class,Stand.class两个文件,当我们需要使用B作为参数传入时,首先就需要去编译B.java

~/Desktop/sourceCode/java-test/src/main/java/com/test/dynamicLoading> javac B.java Stand.java

然后执行dynamicRunAll注意需要传入的参数需要为该类的全限定名,类似绝对路径的意思,这里我们传入的参数是com.test.dynamicLoading.B

~/Desktop/sourceCode/java-test/src/main/java> java com.test.dynamicLoading.dynamicRunAll com.test.dynamicLoading.B

获取该类的名称:com.test.dynamicLoading.B
该类的方法有:private void com.test.dynamicLoading.B.privateRun()
该类的方法有:public void com.test.dynamicLoading.B.run()
B running

Ok,完美执行,现在我们有个新需求,需要增加一个C类当做参数的判断,在上一节中已经建了一个C.java直接拿来用,但是我们不再需要去修改执行文件,也不需要再次去编译执行文件,也就是说不需要中断主服务(加载的类信息可以在系统运行过程中动态添加),我们只需要把这个新写的类编译一下,然后就可以直接使用即可

  • 首先编译C.java
~/Desktop/sourceCode/java-test/src/main/java/com/test/dynamicLoading> javac C.java Stand.java
  • 其次当做参数直接传入
~/Desktop/sourceCode/java-test/src/main/java> java com.test.dynamicLoading.dynamicRunAll com.test.dynamicLoading.C

获取该类的名称:com.test.dynamicLoading.C
该类的方法有:public void com.test.dynamicLoading.C.run()
C running

这个过程中不需要改动dynamicRunAll.java,也不需要重新编译,使用反射实现的方式非常的优美。

总结

java的反射作用的地方有很多,比如注解中也有广泛的运用,而且和注解强相关的一些框架如spring中使用的也非常频繁,通过反射获取执行的类然后生成类实例只是反射中一部分的作用而已;反射的理念有点像是一种代理,本身真实的对象并不需要显性的露面,也有点像IOC,把需要执行的动作交给调用方,不是自己把所有类都加载完毕,然后挑某一个执行,而是调用方想执行哪一个类,就去加载该类,然后再执行;打一个比方:静态加载就像,你(调用方)去饭店吃饭,厨子(执行类)不需要把菜单上的所有菜(调用类)都炒好(静态加载),然后你选一个想吃的拿过来吃;动态加载就像,你从菜单中点其中一个菜,然后告诉厨子要吃这个,厨子再进行做菜;这是我的浅显理解,请不吝赐教

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java中反射有什么作用? 的相关文章

随机推荐

  • 嵌入式软件面试题整理

    基础试题 1 用预处理指令 define 声明一个常数 xff0c 用以表明1年中有多少秒 xff08 忽略闰年问 define SECONDS PER YEAR 60 60 24 365 UL 说明 xff1a define 语法的基本知
  • 硬件在环仿真(HiL)测试介绍

    一 HiL是什么 xff1f 硬件在环仿真 xff08 Hardware in the Loop xff0c 简称HIL xff09 是真 的控制器连接假 的被控对象 xff0c 以一种高效低成本的方式对控制器进行全面测试 它是一种用于复杂
  • Docker Dockerfile详解

    dockerfike快速创建自定义的Docker镜像 一 目录 1 docker典型结构 2 指令介绍 3 创建docker镜像 二 结构 DockerFile分为四部分组成 xff1a 基础镜像信 维护者信息 镜像操作指令和容器启动时执行
  • 树莓派3B+(Raspberry Pi 3 Model B+)安装Ubuntu MATE 18.04及简单配置

    树莓派3B 43 安装Ubuntu MATE 18 04及简单配置 安装Ubuntu18 04 MATE下载Ubuntu 18 04 MATE准备Raspberry Pi Imager镜像烧录工具开机启动 简单配置设置root密码更新列表配
  • OpenCV之getOptimalNewCameraMatrix

    去畸变后的图像四周会出现黑色区域 xff0c 如果畸变较大 xff0c 如鱼眼镜头 xff0c 四周黑色区域将会更大 opencv中给我们提供了一个函数getOptimalNewCameraMatrix xff0c 用于去除畸变矫正后图像四
  • 【深度学习小常识】什么是mAP?

    目录 一 mAP相关概念 1 正例与负例 2 P xff08 精确率 xff09 3 R xff08 召回率 xff09 4 ACC xff08 准确率 xff09 5 AP xff08 平均精确度 xff09 6 示例 二 mAP 1 m
  • STM32CubeMX 下载和安装 详细教程

    HAL库 STM32CubeMX开发 STM32F407 目录 STM32CubeMX安装包 Win 6 6 1 下载链接 STM32CubeMX 下载 步骤1 xff1a 点击官网链接下载 官网下载地址 xff1a https www s
  • Keil5----跳转定义和查找功能

    一 Keil5 跳转定义 跳转定义 鼠标左键点击要查找的变量 方法1 xff1a 点击鼠标右键 xff0c 功能栏中有跳转定义的选项 方法2 xff1a 按快捷键 F12 具体操作如下图所示 xff1a 跳转结果 二 Keil5 查找功能
  • 使用WIFI模块AT指令进行MQTT协议通信

    劢领系列模组 xff0c 经过1年多的演化后 xff0c 已存在多套标准的固件程序 如果用户需要使用MQTT方式进行通信 xff0c 则需要选择标准AT指令 43 MQTT的版本 此版本不仅可以支持标准AT指令的SOCKET通信 xff0c
  • ActiveMQ配置wss

    最近把前端页面由原来的http升级为了https xff0c 发现之前ActiveMQ提供的ws不能强求了 xff0c https服务下要求升级到wss 全网搜索了下 xff0c 没有找到一个靠谱的文档 一 证书准备 使用wss连接服务必须
  • stm32使用HAL库快速编写智能寻迹避障小车(附代码)

    最近学校安排了一节用stm32编写寻迹避障小车的课 xff0c 但无奈学校老师教的方法让作者觉得无法理解 xff0c 但课程答辩时间快到了 xff0c 组内小组成员又做的磕磕绊绊 xff0c 于是身为组长的我就决定尝试一下用刚学的cubem
  • [hal库]使用 CubeMX 快速生成 FreeRTOS 系统并实现多任务处理

    由于项目需求 xff0c 需要使用FREERTOS搭载轻量系统 xff0c 因此写此博客给大家提供一个快速搭建RTOS系统的方法 xff0c 通过cubemx快速生成 以下内容包括 xff1a FreeRTOS 简介 程序框图所需要的元器件
  • Windows C/C++ CLion 开发环境搭建

    博文目录 文章目录 IDE CLion安装设置MinGW 插件测试 特殊配置 使用 CLion 开发 C 43 43 CUDA 应用注意不要走如下弯路 IDE CLion 安装 官网 官方全版本下载 CLion 2021 2 3 exe 或
  • git常见报错解决办法,fatal: the remote end hung up unexpectedly

    问题一 xff1a 上传GIT项目报fatal the remote end hung up unexpectedly错误 上传项目报fatal the remote end hung up unexpectedly的错误 xff0c 应该
  • AtCoder从小白到大神的进阶攻略

    摘自https www cnblogs com LHYLHY p 11572011 html 在此对作者表示感谢 AtCoder从小白到大神的进阶攻略 前言 现在全球最大的编程比赛记分网站非CodeForces和AtCoder莫属了 xff
  • 2020java面试总结

    博主背景 xff1a 92年生 xff0c 渣本毕业 xff0c java岗 xff0c 经验接近6年 xff0c base上海 本文宗旨 xff1a 本文旨在将博主最近的面试经历分享给大家 xff0c 并作些总结 xff0c 尽量为在准备
  • odroid上mavros指定版本安装

    mavros 安装 指定版本 xff1a 0 16 0 https github com mavlink mavros 以下是可能出现的错误 error1 can t find mavlinkConfig cmake solution ht
  • FreeRTOS源码解析 -> vTaskDelete()

    vTaskDelete API 函数 任务可以使用API函数vTaskDelete 删除自己或其它任务 任务被删除后就不复存在 xff0c 也不会再进入运行态 空闲任务的责任是要将分配给已删除任务的内存释放掉 因此有一点很重要 xff0c
  • 设置华为交换机使用账号密码方式进行SSH登录

    1 创建rsa本地密钥对与创建账号 Huawei rsa local key pair create The key name will be Huawei Host The range of public key size is 512
  • java中反射有什么作用?

    前言 反射blog有很多 xff0c 不再赘述 xff0c 但是反射的作用具体实现场景就会比较少 xff0c 这里举个例子 一个需求 使用参数的方式传入需要执行的类名 xff0c 然后执行相应类的同名方法 普通的实现方法 静态加载 因为需要