首页 > 其他分享 >反射

反射

时间:2024-08-07 16:27:49浏览次数:9  
标签:反射 Java 对象 动态 Class 加载

反射

1.类加载机制

Java中的类加载机制是Java运行时的核心组成部分,它负责在程序运行过程中动态加载和连接类文件,并将其转换为可执行代码。这一机制遵循“按需加载”的原则,即只有在需要用到某个类的时候,才会将这个类的相关信息加载到内存中。

1.1.类的生命周期

Java中的类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括7个阶段:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)。其中,加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的,而解析阶段则不一定在初始化之前完成,以支持Java的动态绑定。

1.2.类加载的时机

类加载的时机通常是在以下情况发生时:

  1. 主动引用

    • 创建类的实例(即使用new关键字)。
    • 访问类的静态变量或静态方法。
    • 反射(如使用Class.forName())强制加载类。
    • 初始化一个类的子类(会首先初始化子类的父类)。
    • JVM启动时,包含main方法的类被加载。
  2. 被动引用

    • 通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。
    • 定义对象数组和集合,不会触发该类的初始化。
    • 类A引用类B的静态常量(注意静态常量必须是字面值常量,否则还是会触发B的初始化)。

1.3.类加载的过程

类加载过程包括5个阶段:加载、验证、准备、解析、初始化。

  1. 加载阶段

    • 通过类的全限定名获取定义该类的二进制字节流。
    • 将该字节流表示的静态存储结构转换为Metaspace(元空间)区的运行时存储结构。
    • 在内存中生成一个代表该类的java.lang.Class对象,作为元空间区中该类各种数据的访问入口。
  2. 验证阶段

    • 确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
    • 包括文件格式验证、元数据验证、字节码验证和符号引用验证。
  3. 准备阶段

    • 为类的静态变量分配内存并设置初始值(通常是数据类型的零值,final修饰的静态变量除外)。
    • 不包括实例变量的分配,实例变量将在对象实例化时分配。
  4. 解析阶段

    • 将常量池内的符号引用替换为直接引用的过程。
    • 符号引用是以一组符号来描述所引用的目标,直接引用则是指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。
  5. 初始化阶段

    • 真正开始执行类中定义的Java程序代码。
    • 初始化阶段是执行类构造器<clinit>()方法的过程。
    • <clinit>()方法是由编译器自动收集类中所有类变量的赋值动作和静态语句块中的语句合并产生的。

1.4.类加载器

Java中的类加载是通过类加载器(ClassLoader)及其子类来完成的。类加载器具有层次结构和加载顺序,遵循双亲委派模型。主要的类加载器包括:

  1. 启动类加载器(Bootstrap ClassLoader)

    • 负责加载Java运行时核心类库,如rt.jar
    • 由C++实现,不是ClassLoader的子类,无法直接获取。
  2. 扩展类加载器(Extension ClassLoader)

    • 负责加载Java虚拟机扩展目录(如<JRE_HOME>/lib/ext)中的类库。
    • 继承自java.net.URLClassLoader
    • 开发者可以直接使用
  3. 应用程序类加载器(Application Class Loader)

    • 负责加载应用程序的类和资源文件,即classpath路径下的类。
    • 继承自java.lang.ClassLoader
    • 开发者可以直接使用
  4. 自定义类加载器

    • 开发人员可以根据需要自定义类加载器,以实现特定的类加载策略。

2.什么是反射

Java中的反射(Reflection)是一种强大的机制,它允许程序在运行时(runtime)动态地访问和操作类的内部属性、方法以及构造器等。这种机制使得Java具有更高的灵活性和动态性。(new方法创建的对象是在编译时加载)

2.1.定义

Java反射是Java语言的一个重要特性,它使得Java程序在运行时能够访问、检测和修改它本身的结构和行为。通过反射,程序可以获取任意类的内部信息(如属性、方法、构造器等),并可以动态地创建对象、调用方法以及访问和修改对象的属性。

2.2.主要功能

  • 获取类的信息:反射可以获取类的名称、包信息、所有属性、方法、注解、类型、类加载器等。
  • 动态创建对象:通过反射可以在运行时动态地创建类的对象实例。
  • 调用方法:反射允许程序在运行时调用对象的任意方法,包括私有方法。
  • 访问和修改属性:反射还可以访问和修改对象的私有属性。
  • 判断对象类型:通过反射可以判断任意一个对象所属的类。

2.3.使用场景

  • 动态代理:反射可以用于实现动态代理,即在不修改原有代码的基础上,为接口或类添加额外的功能。这在AOP(面向切面编程)中非常有用。
  • 框架开发:许多流行的Java框架(如Spring、Hibernate等)都利用了Java反射技术来实现对配置文件的解析、对注解的支持、对自定义类型的支持等功能。
  • 性能优化:反射可以用于性能优化,例如在运行时动态加载类、创建对象、调用方法等,从而避免了在编译时进行这些操作所需的时间和资源。
  • 工具开发:反射可以用于开发一些实用的工具类,如序列化和反序列化工具、ORM框架的通用数据访问层等。
  • 热部署:反射可以用于实现热部署,即在不重启应用服务器的情况下,实时更新编译后的字节码文件。
  • 单元测试:反射可以用于编写单元测试,通过反射可以动态地创建对象、调用方法、访问属性和修改字段值等,从而更方便地进行测试。

2.4.注意事项

  • 性能问题:反射操作通常比直接代码调用要慢,因为它需要额外的步骤来获取类的信息并执行相应的操作。因此,在性能要求较高的场景中应谨慎使用反射。
  • 安全性问题:反射机制可以访问类的私有属性和方法,这可能会破坏封装性并导致安全问题。因此,在使用反射时应确保代码的安全性。
  • 可读性和可维护性问题:过度使用反射可能会使代码变得复杂和难以阅读和维护。因此,在使用反射时应权衡其带来的好处和代价。

3.反射的原理

Java反射的原理主要基于Java运行时环境(JRE)中的类加载机制和JVM(Java虚拟机)的能力,它允许程序在运行时动态地加载类、获取类的详细信息,并操作类或对象的属性和方法。

3.1.反射的主要步骤

  1. 获取Class对象:
    • 可以使用Class.forName(String className)方法,通过类的全限定名来加载类并获取Class对象。
    • 可以使用类名.class来获取Class对象,例如String.class
    • 还可以使用对象的getClass()方法来获取其对应类的Class对象,例如String str = "hello"; Class<?> cls = str.getClass();
  2. 获取类的信息:
    • 通过Class对象可以获取类的各种信息,如字段(Field)、方法(Method)、构造函数(Constructor)等。
    • 这些信息是以对象的形式提供的,如Field对象代表类的字段,Method对象代表类的方法等。
  3. 操作类的属性和方法:
    • 可以使用反射机制来动态地创建对象、调用方法、访问和修改类的属性等。
    • 对于私有属性和方法,可以使用setAccessible(true)方法来关闭Java的访问检查,从而进行访问和修改。

3.2.原理概述

Java反射的原理是通过在JVM中加载类的字节码文件,并通过这些文件生成Class对象。Class对象作为反射操作的基础,提供了丰富的API来获取和操作类的内部信息。通过这些API,程序可以在运行时动态地加载类、创建对象、调用方法、访问和修改类的属性等。这种机制大大增强了Java的灵活性和可扩展性,但也需要注意其带来的性能开销和安全性问题。

4.Class对象

在Java中,每个类都有一个与之对应的Class对象。这个Class对象包含了类的所有信息,包括类的属性、方法、构造函数等。它是反射操作的基础,通过Class对象可以获取类的详细信息,并进行相应的操作。

4.1.三种获取Class对象的方式

在Java中,有三种主要的方式来获取一个类的Class对象,这是进行反射操作的第一步。

  1. 使用Class.forName(String className)方法

    这是最常用的动态加载类的方式。你需要提供一个类的全限定名(包括包名)作为字符串参数。Class.forName()方法会加载与给定字符串名称相关的类,并返回该类的Class对象。如果找不到该类,则会抛出ClassNotFoundException

    try {
        Class<?> cls = Class.forName("java.lang.String");
        System.out.println(cls.getName()); // 输出 java.lang.String
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    

    注意:这种方法会触发类的初始化。

    简单灵活(反射推荐使用)

  2. 使用.class语法

    对于已知类型,可以直接使用类型名称后跟.class来获取其Class对象。这种方式在编译时就已经确定了要操作的类,因此不会触发类的加载过程(除非这个类还没有被加载过),但会返回该类的Class对象。

    Class<String> cls = String.class;
    System.out.println(cls.getName()); // 输出 java.lang.String
    

    注意:这种方式不会触发类的初始化。

    需要导入类包依赖太强

  3. 使用对象的getClass()方法

    如果你有一个类的实例,可以使用该实例的getClass()方法来获取它的Class对象。这种方式非常直接,因为每个Java对象都有一个getClass()方法,它返回表示该对象实际类的Class对象。

    String str = "Hello, World!";
    Class<?> cls = str.getClass();
    System.out.println(cls.getName()); // 输出 java.lang.String
    

    注意:这种方式也不会触发类的初始化(如果该类已经被加载并初始化过了)。

    已经new了对象,没必要用反射

总结:

  • Class.forName()是在运行时动态加载类并获取其Class对象的方式,它可能会触发类的初始化。
  • .class语法是在编译时就确定了要操作的类,它不会触发类的加载(除非类还未被加载),但会返回类的Class对象,且不会触发类的初始化。
  • getClass()方法是通过对象的实例来获取其Class对象的方式,它也不会触发类的初始化(如果类已经加载并初始化过了)。

4.2.两种创建Class对象的方式

5.反射的优点和缺点

5.1.优点:

  1. 提高灵活性和扩展性:
    • 反射允许程序在运行时动态地加载类、创建对象、调用方法、访问和修改类的属性等,这大大提高了程序的灵活性和扩展性。
    • 开发者可以编写更通用的代码,这些代码可以在不知道具体类信息的情况下工作。
  2. 简化框架开发:
    • 许多Java框架和库都利用反射来简化开发过程。例如,Spring框架使用反射来自动地管理对象的生命周期和依赖关系。
    • 反射使得框架能够动态地处理类的加载和对象的创建,从而减少了开发者需要编写的样板代码。
  3. 增强的测试和调试能力:
    • 在进行单元测试和集成测试时,反射可以用于模拟和验证类的行为,而无需修改类的源代码。
    • 开发者可以使用反射来访问和修改类的私有属性和方法,以便进行深入的调试和测试。

5.2.缺点:

  1. 性能开销:
    • 反射操作通常比直接代码调用要慢,因为它需要额外的步骤来获取类的信息并执行相应的操作。
    • 反射调用涉及类型检查、安全检查等额外工作,这些都会增加程序的执行时间。
  2. 安全性问题:
    • 反射机制可以访问类的私有属性和方法,这可能会破坏封装性并导致安全问题。
    • 如果不当地使用反射,可能会暴露敏感信息或允许未授权的访问。
  3. 可读性和可维护性:
    • 过度使用反射可能会使代码变得复杂和难以阅读,因为反射调用通常不如直接代码调用那样直观。
    • 反射代码可能更难于调试和维护,因为开发者需要理解反射的工作原理以及它如何影响程序的执行流程。
  4. 依赖性问题:
    • 反射依赖于类的具体实现,如果类的结构发生变化(如方法重命名、参数变更等),则使用反射的代码可能会失效。
    • 这要求开发者在修改类时不仅要考虑直接调用这些类的代码,还要考虑可能通过反射调用这些类的代码。

6.反射的用途

6.1. 框架设计

在框架设计中,反射技术被广泛应用以实现解耦,提高框架的可扩展性和灵活性。框架通常需要在运行时动态地加载和使用各种类,而不需要在编译时知道这些类的具体信息。反射允许框架根据配置或约定动态地创建对象、调用方法、访问属性等,从而实现了高度的灵活性和可扩展性。

6.2. 单元测试

在单元测试中,反射技术可以用来访问类的私有或受保护成员,使得测试更加全面。由于Java的封装性,通常不能直接访问类的私有成员,但通过使用反射,可以在测试中绕过这些限制,对类的内部状态和行为进行验证。

6.3. 动态代理

反射技术还可以用来创建动态代理对象,实现AOP(面向切面编程)等功能。动态代理允许在运行时动态地创建一个实现了指定接口的代理对象,该代理对象可以在调用目标方法前后执行特定的逻辑,如事务管理、安全检查等。

6.4. 访问和修改类的属性及方法

通过反射,可以在运行时动态地访问和修改类的属性及方法,这对于一些需要动态操作对象属性的场景非常有用。例如,在开发一些通用的数据处理框架时,可能需要根据配置动态地访问对象的属性并进行处理。

6.5. 序列化和反序列化

许多Java序列化和反序列化工具都是基于反射机制实现的。通过反射,可以在运行时动态地获取对象的类型信息,并将其转换为可存储或传输的格式。反序列化时,再根据类型信息动态地创建对象并恢复其状态。

6.6. 插件系统

在开发插件系统时,反射也扮演着重要角色。插件系统通常需要在运行时动态地加载和使用插件,而不需要在编译时知道插件的具体信息。通过反射,可以实现插件的动态加载和卸载,以及插件之间的交互。

6.7. 跨语言交互

在某些情况下,反射还可以用于实现跨语言的交互。例如,在Java中通过JNI(Java Native Interface)调用本地代码时,可以利用反射机制在Java端动态地创建和管理本地代码所需的参数和返回值。

需要注意的是,虽然反射提供了强大的灵活性和扩展性,但它也带来了一定的性能开销和安全性风险。因此,在使用反射时应该权衡其利弊,确保在合理的场景下使用,并采取相应的措施来降低性能开销和安全风险。

标签:反射,Java,对象,动态,Class,加载
From: https://www.cnblogs.com/tubby233/p/18347305

相关文章

  • 反射
    反射1.理解Class类–对象照镜子后可以得到的信息:某个类的数据成员名、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个类的有关信息。–Class对象只能由系统建立对象–一个......
  • 雷达气象学(7)——反射率因子图分析(气象回波篇)
    从本篇文章开始介绍反射率因子图(即雷达回波强度图)的分析与识别方法。目录7.0雷达回波的分类7.1层状云降水回波7.2积状云降水回波(对流性降水回波)7.3层积混合降水回波7.4零度层亮带7.5晴空回波7.0雷达回波的分类雷达回波可分为气象回波和非气象回波:\[雷达回波\begin{cas......
  • 反射内存卡的优势与特点
    特点1.高速数据传输:‌反射内存卡能够实现极高的数据传输速度,‌这是其最显著的特点之一。‌数据传输速度可达到微秒级或百纳秒级,‌大大提升了数据处理的实时性。‌这种高速传输能力使得反射内存卡非常适用于需要实时数据共享和处理的场合12。‌2.低延迟:‌反射内存卡通过纯硬件......
  • GE 反射内存卡的特点
    .高效数据处理能力:‌能够快速、‌实时地传输和处理数据,‌满足各种应用需求。‌.高度可配置性:‌支持1到8口的SFP收发器配置,‌适应不同通信距离和模式。‌.自动旁路功能:‌在检测到故障时,‌能自动旁路故障节点,‌确保网络稳定运行。‌.可扩展性:‌支持级联,‌最多可支持256个节点的网......
  • 【Java基础知识4】反射
    一、反射机制Java反射机制是指在程序的运行过程中,对于任意一个类,都能够知道它的所有属性和方法;对于任意一个对象,都能够知道调用它的任意属性和方法,这种动态获取信息以及动态调用对象方法的功能称为JAVA语言的反射机制二、反射的核心内容反射的核心内容是JVM在运行时动态......
  • 反射内存卡应用案例
    飞机动力系统半实物仿真:在美国空军的相关应用中,涡轮发动机模型运行在RedhawkLinux软实时环境中,而转子模型则运行在Speedgoat硬实时环境里,两者之间通过反射内存卡来同步数据。通过转子动力学模型,用户能够灵活地调整发动机和发电机之间的功率与转速。特别是当有代表性的动力系......
  • 反射内存卡技术参数
    1.内存容量:‌如128MB、‌256MB、‌512MB等,‌部分高端型号可能提供更大容量。‌2.数据传输速率:‌支持高速数据传输,‌速率范围可能从几十MB/s到几百MB/s不等,‌具体取决于数据包大小和配置。‌3.接口类型:‌包括PCI、‌PCIe、‌CPCI、‌PMC等,‌支持不同的总线标准。‌4.光纤网络支......
  • 如何判断反射内存卡是否安装成功
    要判断反射内存卡是否安装成功,可以通过以下几种方法:在操作系统中查看设备管理器:Windows系统:1.按下Win+X组合键,选择“设备管理器”。在设备管理器中,查找“网络适配器”、“存储控制器”或相关的类别,看是否有与反射内存卡对应的设备显示,且没有黄色感叹号或问号等异常标......
  • python面向对象之反射
    反射,也叫自省。吾日三省吾身,说白了就是自己检测自己,这就叫反射。千万别被这个抽象的名词给吓到了,没什么牛逼的。反射的四种方法:1>hasattr(obj,name)#判断obj.name是否存在,name指代属性,包括数据属性和函数属性。2>getattr(obj,name)#获取obj.name的结果,如果是函数属性,则加上......
  • 注解与反射
    注解与反射1.注解(Annotation)可以被其他程序读取在哪里使用:可以附加在package,class,method,field等上面,相当于给他们添加了额外的辅助信息。可以通过反射机制编程实现对这些元数据的访问。1.1常见内置注解@Override:重写的注解。@Deprecated:废弃的注解。已过时或者有......