Java核心(四)反射

这篇内容叫反射也不够准确,其实它更像是java类加载的一个延申。

Java类加载过程

之前解释过一个Java的类的加载过程,现在回顾一下类的加载:

类的加载指的是将类的字节码文件(.class文件)中数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象。下面来看一下这个Class对象

Class对象

在java世界里,一切皆对象。从某种意义上来说,java有两种对象:实例对象和Class对象。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。

所谓Class对象可以理解为一个Java类在JVM层面上的存在形态,它以虚拟机能理解的形式描述着一个类。运行时产生的每个实例都会通过类元数据中的对象头来记录自己所属的Class对象,我们编写的Java类、这个类的Class对象、这个类在运行时所诞生的具体对象,这三者之间的关系:

每一个类都有一个Class对象,每当编译一个新类就产生一个Class对象,基本类型 (boolean, byte, char, short, int, long, float, and double)有Class对象,数组有Class对象,就连关键字void也有Class对象(void.class)。Class对象对应着java.lang.Class类,如果说类是对象抽象和集合的话,那么Class类就是对类的抽象和集合。

Class类没有公共的构造方法,Class对象是在类加载的时候由Java虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。

在类加载阶段,类加载器首先检查这个类的Class对象是否已经被加载。如果尚未加载,默认的类加载器就会根据类的全限定名查找.class文件。在这个类的字节码被加载时,它们会接受验证,以确保其没有被破坏,并且不包含不良java代码。

Class对象有以下特点:

  1. Class 对象只能由系统建立对象
  2. 一个类在 JVM 中只会有一个Class实例

对象的形成过程

这里要声明一点,自然运行产生的每个实例并不是由Class对象创造,而是JVM通过解析语句,发现'new'操作时,进行创建,创建过程包括:

  • 分配内存:JVM为新对象在堆内存中分配足够的空间。
  • 初始化对象头:设置对象的元数据信息,如对象的类型信息等。
  • 执行构造器<init>():调用对象的构造器方法,进行对象的初始化。

对象头包含了对象的元数据信息,其中就包括指向该对象Class对象的引用,这个引用允许JVM和Java程序识别对象的类型。

反射的本质

在前面说 Java类加载的时候提过,说过类的加载指的是将类的字节码文件(.class文件)中数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类加载的最终结果就是class对象,而关于Class对象的使用就涉及反射的内容了。

正射

理解反射,首先得知道它的对立面,“正射”,前面了解过了,系统会为每个java类自动创建唯一一个Class对象,它包含了与类有关的信息。此时的java类处于一个中间状态,并不是我们使用的对象,只有当我们使用  “ new  Object()”时,才会在JVM堆中根据这个Class对象来产生真正供我们使用的实例对象。其实也就是上面部分的对象的形成过程。

正射的使用含义是,我事先定义了一个对象的某些东西,然后当我需要的时候,我会通知内存去创建这个对象,然后我事先知道这个对象有什么,所以我会精准的调用它的某个方法,某个成员变量。看一个例子:

class Human {String name;int age;String nation;Human(String name,int age,String nation) {this.name=name;this.age=age;this.nation=nation;}void changeName(String name){this.name=name;}
}public class Main {public static void main(String[] args){Human human=new Human("张三",22,"中国");human.changeName("李四");}
}

在上面Main类的main方法中,之所以可以直接写human.changeName(“张三”) 是因为Human类是我设计编写的,作为编写者清楚的知道human作为Human的实例对象,可以调用changeName方法,如果我手抖写错了changeName的方法名,我也会立即改回来,因为我知道Human里没有这个方法。假如我不知道Human里有没有一个改名字的方法,即Human类对我来说是不透明的第三方类,我尝试性的在程序中调用了一个newName方法,保存、编译。这时候编译器会通知我,这样写程序是不对的,Human里没有一个叫newName的方法,编译失败。 

反射

所谓反射,官方的定义是: 指计算机程序在运行时(runtime) 可以访问、检测和修改它本身状态或行为的一种能力。通俗说,反射就是程序在运行的时候能够“观察”并且修改自己的行为,是程序对自身的反思、自检、修改。

反射则与正射相反,是一个类于我而言是透明的,我全然不知道它有什么,反射就像一个镜面一样,告诉我它有什么,像官方解释的更准确地理解,就是当程序运行之后,通过“镜面”,仍旧可以在运行时,插足JVM内部的实例对象相关信息。

反射的应用

像上文中提到的,Java的反射机制十分强大,允许程序在运行时动态地访问和操作类、接口、方法、构造函数和字段。使用反射的切入点就是上文中提到的Class对象,利用JVM中的Class对象,可以在运行时,具备动态的处理实例的能力。

反射一般应用在:

  • 框架开发:例如Spring中核心的依赖注入、Mybaits中的ORM映射等
  • 动态代理: Java中的AOP实现

  • 配置文件与代码的映射:将配置文件中的信息映射到Java类中。例如,XML或JSON配置文件中的属性可以动态地设置到Java对象的字段中。

  • 动态类加载:一些组件、框架的动态加载,可能需要在运行时从网络或其他源动态加载类。反射可以用来加载这些类并创建它们的实例。

  • 调用私有内容:在做一些特殊功能或工具时,不可避免的要违背Java的封装特性,就可以使用反射进行实现

针对Class对象反射常用的一些方法:

类/接口名方法名入参类型返回类型描述
ClassforName(String className)StringClass<?>通过类名获取Class对象,可能抛出ClassNotFoundException
ClassgetDeclaredClasses()Class<?>[]获取类中声明的所有内部类和接口的Class对象数组。
ClassgetDeclaredField(String name)StringField获取类中声明的指定公共字段。
ClassgetDeclaredMethod(String name, Class<?>... parameterTypes)String, Class<?>...Method获取类中声明的指定公共方法。
ClassgetDeclaredConstructor(Class<?>... parameterTypes)Class<?>...Constructor<?>获取类中声明的指定公共构造函数。
Fieldget(Object obj)ObjectObject获取对象的字段值,可能抛出IllegalAccessException
Fieldset(Object obj, Object value)Object, Object设置对象的字段值,可能抛出IllegalAccessException
FieldgetName()String获取字段的名字。
FieldgetModifiers()int获取字段的访问修饰符。
Methodinvoke(Object obj, Object... args)Object, Object...Object调用方法,可能抛出IllegalAccessExceptionInvocationTargetException
MethodgetName()String获取方法的名字。
MethodgetModifiers()int获取方法的访问修饰符。
ConstructornewInstance(Object... initargs)Object...T创建类的实例,可能抛出InstantiationExceptionIllegalAccessException
ConstructorgetParameterTypes()Class<?>[]获取构造函数的参数类型。
ArraygetLength(Object array)Objectint返回数组的长度。
Arrayget(Object array, int index)Object, intObject获取数组元素。
Arrayset(Object array, int index, Object value)Object, int, Object设置数组元素。
ModifierisPublic(int mod)intboolean测试是否设置为public。
ModifierisPrivate(int mod)intboolean测试是否设置为private。
ModifierisProtected(int mod)intboolean测试是否设置为protected。
ModifierisStatic(int mod)intboolean测试是否设置为static。
AccessibleObjectisAccessible()boolean检查对象是否可访问。
AccessibleObjectsetAccessible(boolean flag)boolean设置对象是否可访问,如果设置为true,可以绕过Java语言访问检查。
AnnotationgetDeclaredAnnotations()Annotation[]获取类声明的所有注解。
AnnotationgetAnnotation(Class<T> annotationClass)ClassT获取类上指定类型的注解,如果不存在则返回null。

 

关于反射API配套的

下面来看使用反射越级访问私有变量的例子:

定义一个普通的Service类:

public class DemoService{private void secretMethod() {System.out.println("I'm a secret method!");}
}

我们想要在另一个类中调用demoService的secretMethod方法。由于这个方法是私有的,我们无法直接调用它,但可以使用反射来实现:

public class ReflectionDemo {public static void main(String[] args) {try {// 获取SecretKeeper类的Class对象Class<?> secretKeeperClass = Class.forName("com.dome.DemoService");// 获取SecretKeeper类中名为secretMethod的私有方法的Method对象Method secretMethod = secretKeeperClass.getDeclaredMethod("secretMethod");// 改变访问权限,以便可以访问私有方法secretMethod.setAccessible(true);// 创建SecretKeeper类的实例Object secretKeeperInstance = secretKeeperClass.newInstance();// 调用secretMethod方法secretMethod.invoke(secretKeeperInstance);} catch (Exception e) {e.printStackTrace();}}
}

另一个更常见的例子,如果阅读过Spring早期版本源码的同学应该见过,Spring最核心的Bean管理,实现原理就是,根据外部的xml配置文件,将类进行指定,然后通过反射来完成类的实例化以及依赖的处理。这里简化实现一下:

首先定义一个普通的Java类:


public class MyObject {public void doSomething() {System.out.println("MyObject is doing something.");}
}

对应一个用于控制的配置文件:

objectClass=com.example.MyObject

通过反射,动态生产对象实例:


public class ObjectFactory {public static Object createObject(String configFilePath) throws Exception {// 加载配置文件Properties props = new Properties();try (FileInputStream fis = new FileInputStream(configFilePath)) {props.load(fis);}// 获取类名String className = props.getProperty("objectClass");// 使用反射加载类Class<?> clazz = Class.forName(className);// 创建实例Object obj = clazz.getDeclaredConstructor().newInstance();return obj;}
}public class Main {public static void main(String[] args) {try {// 假设配置文件位于当前目录Object myObject = ObjectFactory.createObject("config.properties");// 假设创建的对象有doSomething方法((MyObject) myObject).doSomething();} catch (Exception e) {e.printStackTrace();}}
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言之数组

目录 一、数组的概念 二、一维数组的使用 数组的创建 数组的初始化 数组的使用 三、一维数组在内存中的存储 四、sizeof计算数组元素个数 五、二维数组的使用 数组的创建 数组的初始化 数组的使用 六、二维数组在内存中的存储 七、C99中的变长数组 八、总结 一、…

ComfyUI 宝藏插件之辅助工具

今天我们就来分享下这个 ComfyUI 辅助脚本工具的功能。 插件安装&#xff0c;小伙伴们直接在管理器里搜索「ComfyUI-Custom-Scripts」&#xff0c;点击安装就可以了&#xff0c;这里再告诉小伙伴们一个小技巧&#xff0c;点击名称可以跳转到插件所在的官网哦。 没有安装管理器…

Rust 实战丨并发构建倒排索引

引言 继上篇 Rust 实战丨倒排索引&#xff0c;本篇我们将参考《Rust 程序设计&#xff08;第二版&#xff09;》中并发编程篇章来实现高并发构建倒排索引。 本篇主要分为以下几个部分&#xff1a; 功能展示&#xff1a;展示我们最终实现的 2 个工具的效果&#xff08;构建索…

Python武器库开发-武器库篇之SMB服务暴力破解(五十五)

Python武器库开发-武器库篇之SMB服务暴力破解(五十五) SMB服务&#xff08;Server Message Block&#xff09;是一种用于文件共享、打印机共享和其他资源共享的网络协议。它最初由IBM开发&#xff0c;后来被微软广泛采用。 SMB服务允许多台计算机在网络上共享文件和资源&…

笔记本硬盘对拷:升级硬盘的好方法!

如今电子产品更新换代的速度不断加快&#xff0c;笔记本电脑的配置也日新月异。几年前购买的笔记本硬盘容量350G曾经令你感到相当满意。但时至今日&#xff0c;这样的容量是否已经显得有些落后&#xff1f;如果你想要升级硬盘&#xff0c;笔记本硬盘对拷是一个很好的选择。 需要…

Radis初阶 Radis基本命令与在Springboot中访问Radis

阿里网盘链接 文章目录 初始NoSQL数据库对比MySQL数据库从结构方面&#xff1a;从关系方面&#xff1a;从查询方式&#xff1a;从事物方面&#xff1a; Redis入门Redis数据结构访问Radis通用命令&#xff08;tab键&#xff1a;自动补全&#xff09;KEYSDELEXISTSEXPIRETTL Str…

Liquibase(Oracle SQLcl集成版)简明示例

本文使用的是Oracle SQLcl中集成的Liquibase&#xff0c;而非开源版Liquibase。 Liquibase的快速入门可以参见Liquibase Core Concepts。需要了解一下概念&#xff1a; Change log&#xff1a;基于文本的更改日志文件按顺序列出对数据库所做的所有更改Change set&#xff1a;…

LabVIEW开发中的常见通讯协议详解

介绍LabVIEW开发中常见的通讯协议&#xff0c;包括RS-232、RS-485、I2C、SPI、CAN、Ethernet、Modbus和GPIB等。通过对各协议的具体内容、特点、使用范围及注意事项进行全面解析&#xff0c;帮助开发者选择合适的通讯协议&#xff0c;提高系统性能和可靠性。 1. RS-232 内容&a…

Python基础教程(十五):面向对象编程

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

DataFrames相关介绍文件读取

目录 1.初识DataFrame 2.DataFrame的构造函数 3.数据框的轴 4.CSV文件读取 5.Excel文件读取 1.初识DataFrame &#xff08;1&#xff09;昨天&#xff0c;我们学习了Series。而Pandas的另一种数据类型&#xff1a;DataFrame&#xff0c;在许多特性上和Series有相似之处。 …

Spring应用如何打印access日志和out日志(用于分析请求总共在服务耗费多长时间)

我们经常会被问到这样一个问题。你接口返回的好慢呀&#xff0c;能不能提升一下接口响应时间啊&#xff1f;这个时候我们就需要去分析&#xff0c;为什么慢&#xff0c;慢在哪。而这首先应该做的就是确定接口返回时间过长确实是在服务内消耗的时间。而不是我们将请求发给网关或…

字符串及其应用

内容 编写程序实现字符串的基本运算&#xff1a; (1) 求串的长度、两串连接、串比较、子串匹配&#xff1b; (2) 用库函数直接实现上一步的字符申操作 完整代码 #include <iostream> #include <stdio.h> #include<string.h> using namespace std; #define M…

AI办公自动化:批量在多个Word文档中插入对应图片

工作任务&#xff1a;文件夹中有多个word文档和word文档名称一致的图片&#xff0c;要把这些图片都插入到word文档中 在chatpgt中输入提示词&#xff1a; 你是一个Python编程专家&#xff0c;写一个Python脚本&#xff0c;具体步骤如下&#xff1a; 打开文件夹&#xff1a;F:…

CSS从入门到精通——背景样式

目录 背景颜色 任务描述 相关知识 背景色 编程要求 背景图片 任务描述 相关知识 背景图片 设置背景图片 平铺背景图像 任务要求 背景定位与背景关联 任务描述 相关知识 背景定位 背景关联 简写背景 编程要求 背景颜色 任务描述 本关任务&#xff1a;在本关…

Pytorch解决 多元回归 问题的算法

Pytorch解决 多元回归 问题的算法 回归是一种基本的统计建模技术&#xff0c;用于建立因变量与一个或多个自变量之间的关系。 我们将使用 PyTorch&#xff08;一种流行的深度学习框架&#xff09;来开发和训练线性回归模型。 二元回归的简单示例 训练数据集&#xff08;可获取&…

【git】 OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to github.com:443

修改/etc/hosts文件&#xff0c;删掉以下 192.30.253.113 github.com 192.30.253.113 github.com 192.30.253.118 gist.github.com 192.30.253.119 gist.github.com #172.24.132.179 gerrit.sdp.nd 140.82.112.25 alive.github.com 140.82.114.6 api.github.com 185.199.110.15…

哪里有海量的短视频素材,以及短视频制作教程?

在当下&#xff0c;短视频已成为最火爆的内容形式之一&#xff0c;尤其是在抖音上。但很多创作者都面临一个问题&#xff1a;视频素材从哪里来&#xff1f;怎么拍摄才能吸引更多观众&#xff1f;别担心&#xff0c;今天我将为大家推荐几个宝藏网站&#xff0c;确保你素材多到用…

【Kafka】Kafka生产者-04

【Kafka】Kafka生产者-04 1. 生产者发送消息流程1.1 发送原理 2. 相关文档 1. 生产者发送消息流程 1.1 发送原理 在消息发送的过程中&#xff0c;涉及到了两个线程——main 线程和 Sender 线程。 在 main 线程中创建了一个双端队列 RecordAccumulator。 main 线程将消息发送给…

STM32 MDK Keil5软件调试功能使用(无需连接硬件)

MDK Keil5 在线仿真STM32&#xff08;无需连接硬件&#xff09; 首先点击工具栏的魔术棒配置一下&#xff1a;&#xff08;记得选择自己的STM32芯片类型&#xff09; 开启调试 使用逻辑分析仪查看IO输出 会打开这个界面&#xff0c;点击左边的setup按钮 会打开这个窗口&am…

182.二叉树:二叉搜索树的最小绝对差(力扣)

代码解决 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* Tre…