Java中的反射机制详解

反射的简单demo

  1. 声明的原始类
    class Cat {
    	private String name = "猫猫";
        public int age = 10;
    
        public Cat(){}
    
        public Cat(String name) {
            this.name = name;
        }
    
        public void hi() {
            System.out.println("hi~ " + name);
        }
        public void hi() {
            System.out.println(name + " cry");
        }
    }
    
  2. src目录下的配置文件re.properties
    classfullpath=com.zhl.Cat
    method=hi
    
  3. 编写反射代码
    public class Reflection01 {
        public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
    		// 加载配置文件中的信息
    		Properties properties = new Properties();
            InputStream inputStream = ReflectionQuestion.class.getClassLoader().getResourceAsStream("re.properties");
            properties.load(inputStream);
            String classfullpath = properties.getProperty("classfullpath");
            String methodName = properties.getProperty("method");
            
            // 使用反射创建对象
            Class<?> aClass = Class.forName(classfullpath);
            Object o = aClass.newInstance();
            System.out.println(o.getClass());
            
            // 通过反射获取成员方法
            Method method = aClass.getMethod(methodName);
            method.invoke(o);
    		
    		// 通过反射获取成员变量(不能获取私有的)
            Field ageField = aClass.getField("age");
            System.out.println(ageField.get(o));
    		
    		// 通过反射获取构造器
            // 无参
            Constructor<?> constructor = aClass.getConstructor();
            System.out.println(constructor);
            // 有参
            Constructor<?> constructor2 = aClass.getConstructor(String.class);
            System.out.println(constructor2);
    	}
    }
    

反射机制和传统调用方法的对比

public class Reflection02 {
    public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        m1();
        m2();
    }
    // 传统方法:直接创建对象
    public static void m1() {
        Cat cat = new Cat();
        long begin = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            cat.hi();
        }
        long end = System.currentTimeMillis();
        System.out.println("传统方案耗时:" + (end - begin) + "ms");
    }

    // 反射机制调用方法
    public static void m2() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class<?> aClass = Class.forName("com.zhl.Cat");
        // 无参构造
        Constructor<?> constructor = aClass.getConstructor();
        // 关闭访问检查的开关
        constructor.setAccessible(true);
        Object o = constructor.newInstance();

        Method method = aClass.getMethod("hi");
        method.setAccessible(true);
        long begin = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            method.invoke(o);
        }
        long end = System.currentTimeMillis();
        System.out.println("使用反射方案耗时:" + (end - begin) + "ms");
    }
}

反射的优点和缺点:

  • 优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活。
  • 缺点:使用反射基本是解释执行,对执行速度有影响。

Class类

Class类的对象不是new出来的,是系统创建的,在类加载阶段生成的。
对于某个类的Class类对象,类只加载一次,在内存中只有一份。
每个类的实例都会记得自己是由哪个 Class 实例所生成。

  • 已知一个类的全类名 ,且该类在类路径下,可通过Class类的静态方法forName()获取
    • 应用场景:通过配置文件读取类的全路径,加载类
    String className;
    Class<?> clazz = Class.forName(className);
    
  • 若已知具体的类,通过 类的class获取,该方式最为安全可靠,程序性能最高
    • 应用场景:多用于参数传递,比如通过反射得到对应构造器对象
    Class<Cat> cat = Cat.class;
    
  • 已知某个类的实例,调用该实例的getClass()方法获取Class对象,实例:
    • 应用场景:通过创建好的对象,获取Class对象
    // 运行类型
    Class clazz = 对象.getClass();	
    
  • 其他方式
    获取类加载器,加载指定类 。
    ClassLoader loader =.class.getClassLoader();	
    Class<?> clazz = loader.loadClass("com.zhl.Cat");
    

类加载

  • 基本说明
    反射机制是 java实现动态语言的关键,也就是通过反射实现类动态加载

    1. 静态加载:编译时加载相关的类,如果没有则报错。
      依赖性太强
    2. 动态加载:运行时加载需要的类,如果运行时不用该类,则不报错。
      降低了依赖性
    3. 举例说明 (伪代码)
    String key = scan.next();
    switch(key) {
    	case "1" : 
    		Dog dog = new Dog();
    		dog.hi();
    		break;
    	case "2":
    		Class<?> clazz = Class.forName("Person");
    		Object o = clazz.newInstance();
    		Method methodName = clazz.getMethod("hi");
    		method.invoke(o);
    		break;
    	default:
    		break;
    }
    

    注意这段代码中,并没有创建 DogPerson类(当前module中没有Dog或Person类),但是编译时,只会报 Dog 类没有创建,而Person类并没有监测出来是否被创建,只有真正被执行到这行代码时才会被加载,判断是否创建过Person类。

  • 类加载时机

    1. 当创建对象时(new)
    2. 当子类被加载时,父类也加载
    3. 调用类中的静态成员时
    4. 通过反射(动态加载)

    关于JVM的类加载可参考https://blog.csdn.net/m0_72406127/article/details/137058092?spm=1001.2014.3001.5501

通过反射获取类的结构信息

  • java.lang.Class

    1. getName:获取全类名
    2. getSimpleName:获取简单类名
    3. getFields:获取所有public修饰的属性,包含本类以及父类的
    4. getDeclaredFields:获取本类中所有属性
    5. getMethods:获取所有public修饰的方法,包含本类以及父类的
    6. getDeclaredMethods:获取本类中所有方法
    7. getConstructors:获取所有public修饰的构造器,包含本类
    8. getDeclaredConstructors:获取本类中所有构造器
    9. getPackage:以Package形式返回包信息
    10. getSuperClass:以Class形式返回父类信息
    11. getInterfaces:以Class[]形式返回接口信息
    12. getAnnotations:以Annotation[]形式返回注解信息
  • java.lang.reflect.Field

    1. getModifiers: 以int形式返回修饰符
      默认修饰符是0,public 是 1,private 是 2,protected 是 4,static 是 8 ,final是 16,多个不同的修饰符可以相加
    2. getType:以Class形式返回类型
    3. getName:返回属性名
  • java.lang.reflect.Method

    1. getModifiers:以int 形式返回修饰符
    2. getName:返回方法名
    3. getReturnType:以Class形式获取 返回类型
    4. getParameterTypes:以Class[]返回参数类型数组
  • java.lang.reflect.Constructor

    1. getModifiers: 以int形式返回修饰符
    2. getName:返回构造器名(全类名)
    3. getParameterTypes:以Class[]返回参数类型数组

通过反射创建对象

  • 方式一:调用类中的publi 修饰的无参构造器
  • 方式二:调用类中的指定构造器
  • Class类相关方法
    • newlnstance:调用类中的无参构造器,获取对应类的对象
    • getConstructor(Class…clazz):根据参数列表,获取对应的public构造器对象
    • getDeclaredConstructor(Class…clazz):根据参数列表,获取对应的所有构造器 对象
  • Constructor类相关方法
    • setAccessible: 设为true,可以访问private修饰的构造器
    • newinstance(Object…obj): 调用构造器

在构造器/方法/属性中使用 getDeclaredXxx方法可以获取本类中的所有构造器/方法/属性,
通过setAccessible()方法,设置参数为true,关闭访问检查的开关,就可以直接访问和修改被 private 修饰的构造器/方法/属性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/557635.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Docker 部署 MongoDB 数据库

文章目录 官网地址docker 网络mongod.conf部署 MongoDB部署 mongo-expressdocker-compose.ymlMongoDB shell 官网地址 https://www.mongodb.com/zh-cn docker 网络 # 创建 mongo_network 网络 docker network create mongo_network # 查看网络 docker network list # 容器连…

RT-Thread在Win10下编译出现 unsupported pickle protocol: 5解决方案

调试背景&#xff1a; 在WIN10下编译RT-Thread源码&#xff1a;对象处理器平台是Microchip SAMA5D27-SOM1-EK评估板。 unsupported pickle protocol: 5 编译出现报错:ValueError : unsupported pickle protocol: 5 $ scons scons: Reading SConscript files ... Newlib ver…

MySQL:执行一条查询语句期间发生了什么?

MySQL的架构分为两层&#xff0c;Server 层和存储引擎层 server层负责建立连接、分析和执行SQL&#xff0c;MySQL&#xff0c;MySQL大多数的核心功能模块都在在这里实现&#xff0c;下图上半部分都是server层做的事情&#xff0c;另外&#xff0c;所有的内置函数&#xff08;如…

在mini2440上编写linux应用程序、字符设备驱动程序的编写与编译

在mini2440上编写linux应用程序 结合前两篇的学习&#xff0c;一个linux操作系统已经在mini2440上运行起来了&#xff0c;结合交叉编译环境和nfs等工具&#xff0c;我们可以在mini2440上编写任何我们在linux系统编程中学到的应用程序。一个简要的多文件Makefile文件如下&#…

设计模式——2_9 模版方法(Template Method)

人们往往把任性也叫做自由&#xff0c;但是任性只是非理性的自由&#xff0c;人性的选择和自决都不是出于意志的理性&#xff0c;而是出于偶然的动机以及这种动机对感性外在世界的依赖 ——黑格尔 文章目录 定义图纸一个例子&#xff1a;从文件中获取信息分几步&#xff1f;Rea…

基于Spingboot+vue协同过滤音乐推荐管理系统

项目演示视频效果&#xff1a; 基于Spingbootvue协同过滤音乐推荐管理系统 基于Spingbootvue协同过滤音乐推荐管理系统 1、项目介绍 基于Springboot的音乐播放管理系统总共两个角色&#xff0c;用户和管理员。用户使用前端前台界面&#xff0c;管理员使用前端后台界面。 有推荐…

Golang内存、指针逃逸、垃圾回收机制概览

最近看到了一篇文章是关于go的内存、指针逃逸和垃圾回收机制的&#xff0c;发现自己并未很细致的了解过这方面的内容&#xff0c;于是在翻阅各种文章的情况下&#xff0c;写出了这篇总结&#xff0c;参考文章放在文末&#xff0c;可自取 内存 Go 语言使用一个自带的垃圾收集器…

【S32K3 入门系列】- ADC 模块简介(上)

一、 前言 对于 S32K3 系列的初学者来说&#xff0c;S32K3 系列的参考手册阅读难度是让人望而却步的&#xff0c;本系列将对 S32K3 系列的外设进行逐一介绍&#xff0c;对参考手册一些要点进行解析。本文旨在介绍 S32K3 系列的 ADC 模块&#xff0c; ADC&#xff08;Analog to…

node端导出excel-用请求排队来限流

需求 有一个会执行luckySheet脚本并且导出excel的node接口&#xff0c;会在每天凌晨执行&#xff0c;但是文件过大时会内存溢出 之前有用worker来实现多线程&#xff08;主要是避免变量污染&#xff09;&#xff0c;但这样只能保证主线程不卡死&#xff0c;几个子线程合起来占用…

MDC搭配ttl使用!!!

一、简介 MDC 介绍​ MDC&#xff08;Mapped Diagnostic Context&#xff0c;映射调试上下文&#xff09;是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的Map&#xff0c;可以往其中添加键值对。MDC 中包含的内容可以被…

使用yolov8 进行实例分割训练

1、基于windows 的ISAM标注 直接下载安装包&#xff0c;解压后即可使用 链接&#xff1a;https://pan.baidu.com/s/1u_6jk-7sj4CUK1DC0fDEXQ 提取码&#xff1a;c780 2、标注结果转yolo格式 通过ISAM标注后的json文件路径 原始json格式如下&#xff1a; ISAM.json 转 yolo.…

牛客2024 【牛客赛文X】春招冲刺 ONT34 加油站【中等 贪心 C++、Java、Go、PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/a013a0691a0343aeb262ca1450d2fe4e 思路 贪心&#xff1a; 如果总的gas小于走完全程的cost&#xff0c;直接返回-1不需要再找了 如果确保了可以走完一圈之后&#xff0c;那么从index 0开始找&#xff0c; 当g…

【cygwin】工具安装apt-cyg

目录 下载安装查看是否安装成功安装软件 下载 git clone https://github.com/transcode-open/apt-cyg.git安装 cd apt-cyg mv apt-cyg /usr/local/bin/ 查看是否安装成功 apt-cyg --help安装软件 apt-cyg install nano

视频号小店怎么做?新手开店必备运营攻略,看这一篇就够了

大家好&#xff0c;我是电商笨笨熊 作为腾讯推出的电商项目&#xff0c;视频号小店在推出到现在一直都备受关注&#xff0c;同时也吸引了不少玩家入驻&#xff1b; 毕竟作为一个新平台、新市场&#xff0c;一个适合跑马圈地的红利平台&#xff0c;谁都想在这里分的一杯羹。 …

Linux debian gdb dump

1.开发背景 记录 debian 下应用程序崩溃调试方法 2.开发需求 程序越界可以定位到越界的位置附近 3.开发环境 debian 操作系统&#xff0c;如果不支持需要查看是否存在对应的可执行文件 4.实现步骤 4.1 设置 dump 输出大小 ulimit -c unlimited # 设置输出大小 生成core 文…

一个文生视频MoneyPrinterTurbo项目解析

最近抖音剪映发布了图文生成视频功能&#xff0c;同时百家号也有这个功能&#xff0c;这个可以看做是一个开源的实现&#xff0c;一起看看它的原理吧~ 一句话提示词 大模型生成文案 百家号生成视频效果 MoneyPrinterTurbo生成视频效果 天空为什么是蓝色的&#xff1f; 天空…

上位机图像处理和嵌入式模块部署(智能硬件的介绍)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 目前&#xff0c;用上位机软件虽然可以部署项目&#xff0c;但是它本身有自己的缺点&#xff0c;那就是稳定性差、价格贵。稳定性这部分&#xff0…

深度剖析扫雷游戏的各个知识点(2)

小伙伴们&#xff0c;大家好。这次继续上次的剖析扫雷游戏的知识点。 那么本次咱们主要是讲扫雷中的宏定义&#xff0c;也就是#define这些 首先#define是用来定义一个宏&#xff0c;后面就是类似于和变量一样的常量名&#xff0c;以及最后的数字就是它的值。 定义规则 #def…

数据结构——树和二叉树

目录 前言 一、树概念及结构 1.1树的概念 1.2 树的相关概念 ​编辑 1.3 树的表示 1.4 树的应用 2.二叉树概念及结构 2.1 二叉树概念 2.2 现实中的二叉树 2.3 特殊的二叉树 2.4 二叉树的性质 2.5 二叉树的存储结构 总结 前言 之前我们学习到的数据结构都是线性的…

Linux Makefile

1.开发背景 linux 下编译程序需要用到对应的 Makefile&#xff0c;用于编译应用程序。 2.开发需求 编写 Makefile 编译应用程序 1&#xff09;支持多个源文件 2&#xff09;支持多个头文件 3&#xff09;支持只编译修改的文件&#xff0c;包括源文件和头文件 4&#xff09;支持…
最新文章