Java核心(四)反射

news2025/2/24 7:33:48

这篇内容叫反射也不够准确,其实它更像是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.coloradmin.cn/o/1824326.html

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

相关文章

AI办公自动化:kimi批量搜索提取PDF文档中特定文本内容

工作任务&#xff1a;PDF文档中有资料来源这一行&#xff0c;比如&#xff1a; 资料来源&#xff1a;moomoo tech、The Information、Bloomberg、Reuters&#xff0c;浙商证券研究所 数据来源&#xff1a;CSDN、浙商证券研究所 数据来源&#xff1a;CSDN、arXiv、浙商证券研…

精品丨PowerBI迁移到SSAS

业务场景&#xff1a; 企业初期在进行 BI 可视化路线的时候&#xff0c;往往不会选择方案较为完整的SSAS&#xff0c;而是会选择轻量的 PowerBI 方案&#xff0c;究其根本还是软件成本的问题。 但是随着模型越来越臃肿&#xff0c;维护成本越来越高&#xff0c;有很多模型需要进…

VictoriaMetrics的高可用二进制方式部署

参考文章实现&#xff1a;Prometheus联邦集群VictoriaMetrics集群搭建部署 1.基本信息 涉及机器列表&#xff0c;机器均为本地虚拟机 192.168.56.108 192.168.56.109方案实现如下 涉及模块介绍 vmstorage: 数据存储节点&#xff0c;负责存储时序数据vmselect: 数据查询节点…

智慧公安指挥中心大数据信息化两中心两基地系统方案

1.1 系统建设目标 本系统是一个汇接全市的报警求助的大型通信指挥系统&#xff0c;技术难度较高、可靠性要求高&#xff0c;技术路线的选择至关重要。 在充分考虑XX市公安局的业务需要&#xff0c;利用现代通信及计算机网络技术的基础上&#xff0c;最大程度地实现资源整合、…

HO-VMD-TCN:西储大学轴承故障诊断全流程详解

声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类智能优化算法及其改进的朋友&#xff0c;可关注我的公众号&#xff1a;强盛机器学习&#xff0c;不定期会有很多免费代码分享~ 目录 原理详解 1.数据预处理 2.特征提取 3.故障…

AI Agent学习系列:微信搭配Agent,让微信秒变特工

在之前的文章里介绍了如何把微信变成高考志愿填报小助手&#xff0c;我已经把这个bot发布到了公众号&#xff0c;大家可以直接在公众号消息输入框里提问即可直接使用&#xff0c;如图&#xff1a; 上面说的bot就是智能体&#xff0c;也叫Agent&#xff0c;和英文里特工是一个单…

AMSR-MODIS 边界层水汽 L3 每日 1 度 x 1 度 V1、V2 版本数据集

AMSR-MODIS Boundary Layer Water Vapor L3 Daily 1 degree x 1 degree V1 (AMDBLWV) at GES DISC AMSR-MODIS Boundary Layer Water Vapor L3 Daily 1 degree x 1 degree V2 (AMDBLWV) at GES DISC 简介 该数据集可估算均匀云层下的海洋边界层水汽。AMSR-E 和 AMSR-2 的微波…

2024 年最新基于 LLOneBot NT 框架搭建 QQ 机器人详细教程(更新中)

LLOneBot 概述 llonebot&#xff08;LLOneBot&#xff09;是一个与OneBot&#xff08;也称为CQHTTP&#xff09;协议兼容的机器人框架&#xff0c;它允许开发者使用不同的编程语言&#xff08;如Python、Go、JavaScript等&#xff09;编写机器人应用&#xff0c;并与各种支持 …

观察 jvm 运行时数据区内存大小(native memory tracking)

jvm 运行时数据区 jvm 运行时数据区包括且不限于以下几个部分: 堆(heap): 用于存储对象实例和数组。堆内存的分配和释放由垃圾回收器进行管理。方法区(method area): 用于存储类的信息、静态变量、常量等。jdk 8 后方法区位于 metaspace。虚拟机栈(vm stack): 用于存储方法的…

Mysql之不使用部署在k8s集群的Mysql而是选择单独部署的Mysql的原因

测试准备&#xff1a; 线程组&#xff1a;并发数100&#xff0c;持续时间2min 两个请求&#xff1a;使用k8s集群中的mysql的wordpress对应端口30011 使用单独部署的mysql的wordpress的对应端口为30022 访问同一个博客 测试结果&#xff1a; 汇总报告&#xff1a; 响应时间图&…

16.番外_模拟c语言文件流

16.番外:模拟c语言文件流 一&#xff0c;前言 先来看一下Struct _IO_FILE的定义。 grep -rnw /usr/include/ -e struct _IO_FILE今天我们要做的就是模拟实现FILE及C语言文件操作相关函数 二&#xff0c;开始&#xff01;做吧&#xff01;我们&#xff01; 1.FILE的缓冲区及…

「小爱同学」接入豆包大模型;华为鸿蒙 OS 成为中国第二大操作系统丨 RTE 开发者日报 Vol.224

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE&#xff08;Real-Time Engagement&#xff09; 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文…

【Android面试八股文】你知道如何实现非阻塞式生产者消费者模式吗?

文章目录 这道题想考察什么 ?考察的知识点日常生活中的生产者消费者模式生产者消费者模式简介为什么需要缓冲区?阻塞与非堵塞非阻塞式生产者消费者模式的实现非阻塞式生产者消费者模式的实现阻塞式生产者消费者模式实现特点这道题想考察什么 ? 是否了解非阻塞式生产者消费者…

R可视化:ggpubr包学习

欢迎大家关注全网生信学习者系列&#xff1a; WX公zhong号&#xff1a;生信学习者 Xiao hong书&#xff1a;生信学习者 知hu&#xff1a;生信学习者 CDSN&#xff1a;生信学习者2 介绍 ggpubr是我经常会用到的R包&#xff0c;它傻瓜式的画图方式对很多初次接触R绘图的人来…

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…

高考志愿填报选专业,兴趣爱好和就业前景哪个优先?

每个人都有自己的兴趣与爱好&#xff0c;而高考志愿填报是在为自己选择职业方向。最理想的状态就是把自己的兴趣和爱好与自己的职业统一起来&#xff0c;让兴趣和爱好促进职业的发展&#xff0c;为职业增添动力。但现实生活中&#xff0c;这种理想的状态并不是每个人都能达到的…

Java多线程面试重点-2

16.Synchronized关键字加在静态方法和实例方法的区别? 修饰静态方法&#xff0c;是对类进行加锁&#xff08;Class对象&#xff09;&#xff0c;如果该类中有methodA和methodB都是被Synch修饰的静态方法&#xff0c;此时有两个线程T1、T2分别调用methodA()和methodB()&#x…

http穿透怎么做?

众所周知http协议的默认端口是80&#xff0c;由于国家工信部要求&#xff0c;域名必须备案才给开放80端口&#xff0c;而备案需要固定公网IP&#xff0c;这就使得开放http80端口的费用成本和时间成本变的很高。那么能不能利用内网穿透技术做http穿透呢&#xff1f;下面我就给大…

Stability AI最新的SD3模型存在严重问题 为规避裸体结果导致躯体部分错乱

人工智能 Stability AI 最新的 SD3 Medium 模型存在严重问题&#xff0c;只要生成人物就会出现躯体错乱&#xff0c;这似乎是该公司刻意规避生成裸体图片的结果。目前猜测他们可能在训练过程中就剔除了 NSFW 内容&#xff0c;同时在训练时规避裸体内容进而导致模型也会刻意将人…

Haption Virtuose 6D力反馈设备遥操作机器人应用研究

在虚拟现实和远程操作技术的飞速发展下&#xff0c;力反馈设备成为了一个不可或缺的工具。其中&#xff0c;Haption Virtuose 6D力反馈设备以其卓越的性能和广泛的应用领域&#xff0c;成为了这一领域的佼佼者。本文将详细介绍Haption Virtuose 6D力反馈设备及其在遥操作机器人…