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.xdnf.cn/news/1451927.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【单片机毕业设计选题24007】-基于STM32和阿里云的家庭健康数据监测系统

系统功能: 本课题设计是基于STM32单片机作为控制主体&#xff0c;通过HX711称重模块&#xff0c;HC-SR04超声波测距模块&#xff0c;红外测温&#xff0c;心率传感器等模块通过I2C或SPI接口与STM32进行通信&#xff0c;并读取传感器输出的身高&#xff0c;体重&#xff0c;心率…

ubuntu的内核离线升级

服务器爆出Linux kernel权限提升漏洞&#xff0c;所以需要升级内核。所以我记录一下升级内核的过程。 因为有权限漏洞&#xff0c;所以升级全程离线断网。 一 下载 先下载内核升级文件&#xff0c;打算升级的目标是6.1.76版本 Index of /mainline/v6.1.76/amd64 下载完毕后传…

如何保护云主机安全

在数字化时代&#xff0c;云服务器已成为企业数据存储、处理和传输的重要工具。然而&#xff0c;随着其应用的广泛和深入&#xff0c;云服务器也面临着越来越多的安全威胁。为了应对这些威胁&#xff0c;白名单技术应运而生&#xff0c;成为保护云服务器安全的重要手段。 首先&…

通信设备的网卡

一、网卡的作用 将计算机或者路由器连接到传输介质上的接口&#xff0c;传输介质可以是有线也可以是无线的。 &#xff08;1&#xff09;计算机的网卡 现在的计算机大多有两个网卡&#xff0c;一个是有线网卡一个无线网卡&#xff0c;比如以我们的台式电脑为例 台式电脑千兆网…

SQL Server Management Studio (SSMS) 20.1 - 微软数据库管理工具

SQL Server Management Studio (SSMS) 20.1 - 微软数据库管理工具 请访问原文链接&#xff1a;https://sysin.org/blog/ssms/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org 笔者注&#xff1a;SQL Server 2014 及之前版本内置…

【Kafka】Kafka生产者-04

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

canvas学习

Canvas API 提供了一个通过 JavaScript 和 HTML 的 元素来绘制图形的方式。它可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面。 Canvas 的基本用法 <canvas> 元素 <canvas id"tutorial" width"150" height"150&quo…

【计算机网络仿真实验-实验2.4、2.5】静态路由、动态路由(RIP)

实验2.4 静态路由 1. 实验拓扑图 注意&#xff1a;有些同学不知道两个路由器之间如何用串行DCE(红线)相接&#xff0c;只需要为路由器分别增加新的HWIC-2T接口卡就好了 不知道如何添加物理接口的&#xff0c;可以查看本人计算机网络专栏中【计算机网络仿真实验——实验准备】…

Java面向对象-抽象类和抽象方法

Java面向对象-抽象类和抽象方法 1、代码案例展示2、抽象类和抽象方法的关系&#xff1a; 1、代码案例展示 1、在一个类中会有一类方法&#xff0c;无需重写&#xff0c;直接使用 2、在一个类中会有一类方法&#xff0c;会对这个方法进行重写 3、一个方法的方法体去掉&#xff…

QT打包(windows linux)封包 完整图文版

目录 简介: 一. for windows 1.首先下载组件 2.开始构建Release版本. 3.然后点击构建 4.在文件夹内直接点击exe文件,会报下面的错误,因为缺少dll连接; 5.需要把这个exe单独复制到一个文件夹内, 6.先cd到单独exe所在的文件夹; cd 文件路径 7.然后运行 windeployqt 文…

Linux安装Tomcat和Nginx

目录 前言一、系统环境二、Tomcat安装步骤Step1 安装JDK环境Step2 安装Tomcat 三、Nginx安装步骤四、测试4.1 测试Tomcat4.2 测试Nginx 总结 前言 本篇文章介绍如何在Linux上安装Tomcat web服务器。 一、系统环境 虚拟机版本&#xff1a;VMware Workstation 15 ProLinux镜像…

【AI基础】第六步:纯天然保姆喂饭级-安装并运行qwen2-7b

整体步骤类似于 【AI基础】第五步&#xff1a;纯天然保姆喂饭级-安装并运行chatglm3-6b-CSDN博客。 此系列文章列表&#xff1a; 【AI基础】概览 【AI基础】第一步&#xff1a;安装python开发环境-windows篇_下载安装ai环境python 【AI基础】第一步&#xff1a;安装python开发环…

观成科技:基于深度学习技术的APT加密流量检测与分类检测方案

一、前言 近年来APT攻击的案例屡见不鲜&#xff0c;给国家、企业以及个人的利益造成极大威胁。随着流量加密技术的不断成熟&#xff0c;许多APT组织倾向于将流量加密后进行传输&#xff0c;从而保护传输内容。由于加密流量的实际载荷已被加密&#xff0c;故采用原始的流量检测…

GO RACE 测试在低版本GCC上报错误 exit status 0xc0000139

windows机器环境&#xff0c;go程序使用race定位时一运行就报错&#xff0c;写了个example如&#xff1a; 能看到加了race之后就不行了&#xff0c; 搜了一下&#xff0c;git上有个issue&#xff1a; runtime: Race detector causes exit status 0xc0000139 on Windows 11 wi…

除了ps我们还可以使用什么方法来处理图片?

照片模糊了怎么办?当照片拍的不好时&#xff0c;容易出现模糊的状况&#xff0c;其实照片模糊了可以通过后期软件加工处理&#xff0c;但是ps操作很复杂&#xff0c;对我们有一定的技术基础要求&#xff0c;那么有没有别的图片处理工具呢&#xff1f; ps它的图片处理功能较为全…

俄罗斯Yandex推广投放如何开户?Yandex广告开户和代运营推广流程详解_俄罗斯_受众_搜索引擎

在俄罗斯进行Yandex广告推广是一种有效的在线营销方式&#xff0c;特别是针对俄罗斯市场。Yandex是俄罗斯最受欢迎的搜索引擎&#xff0c;类似于Google在全球范围内的地位。以下是通过Yandex广告推广的一般步骤&#xff0c;以及如何通过上海上弦进行广告开户和代运营。 1. Yan…

【计算机视觉】人脸算法之图像处理基础知识(一)

图像处理基础知识&#xff08;一&#xff09; 1.图像的构成 图像的构成可以包括以下几方面知识&#xff1a; 1.像素&#xff1a;图像的基本单位&#xff0c;是图像中的一个点。每个像素都有特定的位置和色彩值。在数字图像中&#xff0c;像素的颜色通常由红、绿、蓝&#xf…

基于QT5.12.7的VTK8.2下的VS2015 X64源码编译以及测试

有一段时间没更新博客了&#xff0c;最近在考虑使用VTK作为软件的后处理显示&#xff0c;相比于OSG&#xff0c;VTK在后处理上集成了很多优秀的算法&#xff0c;使用起来比较方便&#xff0c;而且后处理一般不需要太多的交互&#xff0c;所以VTK是一个不错的选择。 之前对VTK了…

彩色打印一般多少钱一张

彩色打印的价格因多种因素而异&#xff0c;包括打印设备的品牌、纸张质量、打印数量以及打印服务提供商的定价策略等。以下是根据参考文章提供的信息&#xff0c;对彩色打印价格的一般性归纳和描述&#xff1a; 设备品牌与型号&#xff1a; 不同品牌和型号的彩色激光打印机价…

Uni-App中的u-datetime-picker时间选择器Demo

目录 前言Demo 前言 对于网页端的推荐阅读&#xff1a;【ElementUI】详细分析DatePicker 日期选择器 事情起因是两个时间选择器同步了&#xff0c;本身是从后端慢慢步入全栈&#xff0c;对此将这个知识点从实战进行提炼 通过Demo进行总结 Demo 用于选择日期和时间的组件&a…