Java—反射机制详解

介绍反射

反射的基本概念

反射(Reflection)是Java语言中的一种机制,它允许程序在运行时检查和操作类、接口、字段和方法等类的内部结构。通过反射,你可以在运行时获取类的信息,包括类的构造器、字段、方法等,并且可以在运行时动态地创建对象、调用方法、访问或修改字段。

反射的核心类和接口

Java反射机制主要涉及以下几个核心类和接口:

  • Class:表示一个类的字节码文件对象,通过它可以获取类的所有信息。
  • Constructor:表示类的构造器对象,通过它可以获取构造器的参数、修饰符等信息,并可以用来创建类的实例。
  • Field:表示类的成员变量对象,通过它可以获取字段的类型、修饰符等信息,并可以用来访问或修改字段的值。
  • Method:表示类的方法对象,通过它可以获取方法的参数、返回值类型、修饰符等信息,并可以用来调用方法。

反射的优点

  • 可扩展性:反射允许程序在运行时动态地加载和使用类,这使得程序具有更好的可扩展性。例如,可以通过配置文件来加载不同的类,从而实现插件化架构。
  • 类浏览器和可视化开发环境:反射可以帮助IDE等开发工具获取类的详细信息,从而提供更好的代码提示、自动补全等功能。
  • 调试器和测试工具:反射可以帮助调试器和测试工具获取类的内部信息,从而实现更强大的调试和测试功能。

反射的缺点

  • 性能开销:反射操作通常比直接调用方法或访问字段要慢得多,因为反射涉及动态解析,JVM无法对其进行优化。
  • 安全限制:反射可以绕过访问控制,访问私有字段和方法,这可能会导致安全问题。
  • 内部暴露:反射可以访问类的内部实现细节,这可能会导致代码的可移植性和稳定性受到影响。

获取元素

获取类

在反射中,获取类的Class对象是第一步。Class对象代表了类的字节码文件,通过它可以获取类的所有信息。获取Class对象有三种主要方式:

通过类名获取:

Class<?> clazz = Class.forName("com.example.MyClass");

这种方式需要类的全限定名(包名+类名),适用于在运行时动态加载类。

通过对象获取:

MyClass obj = new MyClass();
Class<?> clazz = obj.getClass();

这种方式适用于已经有一个类的实例对象的情况。

通过类字面量获取:

Class<?> clazz = MyClass.class;

这种方式适用于在编译时已经知道类名的情况。

Class类下的常用方法:

  • String getSimpleName():获取类的简单名称(不包括包名)。
  • String getName():获取类的全限定名(包括包名)。
  • T newInstance():创建Class对象关联类的实例对象,底层调用无参构造器。注意:这个方法已经被标记为@Deprecated,建议使用Constructor来创建对象。

获取构造器

获取构造器的方法主要通过Class对象来实现:

获取特定构造器:

  • Constructor getConstructor(Class... parameterTypes):根据参数类型获取某个public修饰的构造器。
  • Constructor getDeclaredConstructor(Class... parameterTypes):根据参数类型获取某个构造器,不关心权限修饰符。

获取所有构造器:

  • Constructor[] getConstructors():获取所有public修饰的构造器。
  • Constructor[] getDeclaredConstructors():获取所有构造器,不关心权限修饰符。

Constructor的常用方法:

  • T newInstance(Object... initargs):使用指定的参数创建类的实例对象。
  • void setAccessible(true):设置访问权限,true表示可以访问私有构造器(暴力反射)。
  • String getName():获取构造器的名称。
  • int getParameterCount():获取构造器的参数数量。
  • Class<?>[] getParameterTypes():获取构造器的参数类型数组。

获取成员变量

获取成员变量的方法主要通过Class对象来实现:

获取特定成员变量:

  • Field getField(String name):根据成员变量名获取public修饰的成员变量。
  • Field getDeclaredField(String name):根据成员变量名获取成员变量,不关心权限修饰符。

获取所有成员变量:

  • Field[] getFields():获取所有public修饰的成员变量。
  • Field[] getDeclaredFields():获取所有成员变量,不关心权限修饰符。

Field的常用方法:

  • void set(Object obj, Object value):给指定对象的成员变量赋值。
  • Object get(Object obj):获取指定对象的成员变量的值。
  • void setAccessible(true):设置访问权限,true表示可以访问私有成员变量(暴力反射)。
  • Class getType():获取成员变量的类型。
  • String getName():获取成员变量的名称。

获取方法

获取方法的方法主要通过Class对象来实现:

获取特定方法:

  • Method getMethod(String name, Class... args):根据方法名和参数类型获取public修饰的方法。
  • Method getDeclaredMethod(String name, Class... args):根据方法名和参数类型获取方法,不关心权限修饰符。

获取所有方法:

  • Method[] getMethods():获取所有public修饰的方法,包括父类的方法。
  • Method[] getDeclaredMethods():获取所有方法,不关心权限修饰符,只获取本类声明的方法。

Method的常用方法:

  • Object invoke(Object obj, Object... args):使用指定的参数调用方法,obj是调用方法的对象,args是方法的参数。

反射示例

package com.example;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;// 定义接口,用于打招呼
interface Greetable {void greet(); // 实现打招呼的方法
}// 定义一个父类,用于表示动物
class Animal {protected String species; // 动物的种类// 构造函数,初始化动物的种类public Animal(String species) {this.species = species;}// 让动物发出声音public void makeSound() {System.out.println(species + " 发出声音。");}
}// 定义一个子类,继承自Animal,并实现Greetable接口
class Dog extends Animal implements Greetable {private String name; // 狗的名字// 构造函数,初始化狗的名字public Dog(String name) {super("狗");this.name = name;}// 实现打招呼方法,狗用自己的方式打招呼@Overridepublic void greet() {System.out.println(name + " 说:汪汪!");}
}// 用于演示高级反射用法的示例类
public class AdvancedReflectionExample {// 主函数public static void main(String[] args) {try {// 获取Dog类的Class对象Class<?> dogClass = Dog.class;// 获取构造器并创建实例Constructor<?> dogConstructor = dogClass.getConstructor(String.class);Object dogInstance = dogConstructor.newInstance("小白");// 调用父类的方法Method makeSoundMethod = dogClass.getSuperclass().getDeclaredMethod("makeSound");makeSoundMethod.invoke(dogInstance);// 调用接口方法Method greetMethod = dogClass.getDeclaredMethod("greet");greetMethod.invoke(dogInstance);// 使用反射获取和修改私有字段Field nameField = dogClass.getDeclaredField("name");nameField.setAccessible(true);System.out.println("更新前的名字: " + nameField.get(dogInstance));nameField.set(dogInstance, "小黑");System.out.println("更新后的名字: " + nameField.get(dogInstance));// 创建一个泛型列表并添加Dog对象List<Dog> dogList = new ArrayList<>();dogList.add((Dog) dogInstance);System.out.println("狗狗列表包含: " + dogList.size() + " 只狗。");} catch (Exception e) {e.printStackTrace();}}
}

暴力反射与泛型约束的破坏

泛型在编译阶段提供类型安全,但在运行时被擦除,反射则允许绕过这些安全检查。

核心点:

封装性破坏:通过反射可以访问和修改私有字段和方法,导致原本受保护的对象状态被随意改变。

import java.lang.reflect.Field;class Person {private String name;public Person(String name) {this.name = name;}public String getName() {return name;}
}public class ReflectionEncapsulationDemo {public static void main(String[] args) {Person person = new Person("Alice");// 使用反射访问私有字段try {Field nameField = Person.class.getDeclaredField("name");nameField.setAccessible(true);  // 允许访问私有字段// 修改私有字段的值nameField.set(person, "Bob");// 输出修改后的值System.out.println("修改后的名字: " + person.getName());} catch (Exception e) {e.printStackTrace();}}
}

泛型约束消失:由于泛型类型信息在运行时不可用,使用反射可以插入不符合泛型限制的对象,可能引发类型错误。

package com.example;import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;public class GenericReflectionDemo {public static void main(String[] args) {// 创建一个存储 Double 类型的 ListList<Double> scores = new ArrayList<>();scores.add(99.3);scores.add(199.3);scores.add(89.5);// 使用反射插入一个不符合泛型的字符串try {Class<?> clazz = scores.getClass();Method addMethod = clazz.getDeclaredMethod("add", Object.class);addMethod.invoke(scores, "字符串");  // 插入一个不符合泛型的数据// 输出结果,包含了不符合类型的数据System.out.println("List 内容: " + scores);// List 内容: [99.3, 199.3, 89.5, 字符串]} catch (Exception e) {e.printStackTrace();}}
}

虽然反射可以带来灵活性,但在生产代码中应谨慎使用,以避免引入潜在的错误和安全隐患。

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

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

相关文章

服务器安装pytorch_geometric torch_scatter踩坑记录

conda create -n pyg python3.8.12 pip install torch1.13.0安装的版本如下 pip install torch-scatter pip install torch-sparse pip install torch-cluster pip install torch-spline-conv pip install torch-geometric2.2.0 pip install ipykernel python -m ipykernel i…

Java—注解机制详解

概念 注解是类的组成部分&#xff0c;可以为类携带额外的信息&#xff0c;提供一种安全的注释标记机制&#xff0c;用于将任何信息或元数据与程序元素&#xff08;如类、方法、成员变量等&#xff09;关联。 注解是供编译器或JVM使用的&#xff0c;编译器或JVM可以根据注解执…

云岚到家 第一天实战总结

为什么使用post发送请求&#xff0c;参数依旧会被拼接带url上呢&#xff1f;这应该就是param 与data传参的区别。即param传参数参数会被拼接到url后&#xff0c;data会以请求体传递 不一定post发送请求&#xff0c;数据一定在请求体中&#xff0c;主要是看数据的格式。 后端c…

c++924

2 #include <iostream> #include <cstring>using namespace std;class MyString { private:char *str; // 记录C风格的字符串int size; // 记录字符串的实际长度public:// 定义无参构造MyString() : size(0) {str new char[1];str[0] \0;cou…

《深度学习》—— 神经网络中的数据增强

文章目录 一、为什么要进行数据增强&#xff1f;二、常见的数据增强方法1. 几何变换2. 颜色变换3. 尺寸变换4. 填充5. 噪声添加6. 组合变换 三、代码实现四、注意事项五、总结 一、为什么要进行数据增强&#xff1f; 神经网络中的数据增强是一种通过增加训练数据的多样性和数量…

Python接口自动化测试输出日志到控制台和文件

一、日志的作用 一般程序日志出自下面几个方面的需求&#xff1a; 1. 记录用户操作的审计日志&#xff0c;甚至有的时候就是监管部门的要求。 2. 快速定位问题的根源 3. 追踪程序执行的过程。 4. 追踪数据的变化 5. 数据统计和性能分析 6. 采集运行环境数据 一般在程序上线之后…

2024 年 8 月公链行业研报:Layer 1、比特币 Layer 2 和以太坊 Layer 2 趋势分析

作者&#xff1a;Stella L (stellafootprint.network) 数据来源&#xff1a;Footprint Analytics 公链研究页面 8 月&#xff0c;加密货币市场波动剧烈&#xff0c;比特币和以太坊均遭遇了明显下跌。Layer 1 区块链普遍遭受市场颓势&#xff0c;然而&#xff0c;Tron 却逆势…

考研数据结构——C语言实现插入排序

插入排序是一种简单直观的比较排序算法&#xff0c;它的工作原理是通过构建有序序列&#xff0c;对于未排序数据&#xff0c;在已排序序列中从后向前扫描&#xff0c;找到相应位置并插入。插入排序在实现上&#xff0c;通常采用in-place&#xff08;原地排序&#xff09;&#…

【python篇】python pickle模块一篇就能明白,快速理解

持久性就是指保持对象&#xff0c;甚至在多次执行同一程序之间也保持对象。通过本文&#xff0c;您会对 Python对象的各种持久性机制&#xff08;从关系数据库到 Python 的 pickle以及其它机制&#xff09;有一个总体认识。另外&#xff0c;还会让您更深一步地了解Python 的对象…

【深度】边缘计算神器之数据网关

分布式计算、云边协同、互联互通是边缘计算设备的三项重要特征。 边缘计算设备通过分布式计算模式&#xff0c;将数据处理和分析任务从中心化的云平台下放到设备网关&#xff0c;即更接近数据源的地方&#xff0c;从而显著降低了数据传输的延迟&#xff0c;提高了响应速度和处…

算法4-----综合训练(3)

一&#xff1a;优美的排列 题目&#xff1a; 有1~n的n个整数&#xff0c;用这些数构造一个数组perm&#xff0c;只要构造的数组满足以下两个条件&#xff1a; &#xff08;1&#xff09;i可以被perm[i]整除 &#xff08;2&#xff09;perm[i]可以被i整除 返回其能构造出的…

影刀RPA应用迁移应用复制完整步骤-本地工具

影刀应用迁移工具本地版 不需要输入影刀的用户名和密码就能实现应用的迁移 依赖本地电脑中登录的账号 使用方法 打开软件需要激活,请联系: 左侧选择一个账号选择需要迁移的应用选择目标账号选择要替换的应用 需要用目标账号创建一个空应用,然后在这一步选择点击替换 Q&A…

3. 轴指令(omron 机器自动化控制器)——>MC_MoveRelative

机器自动化控制器——第三章 轴指令 5 MC_MoveRelative变量▶输入变量▶输出变量▶输入输出变量 功能说明▶指令详情▶时序图▶重启运动指令▶多重启动运动指令▶异常 MC_MoveRelative 指定自指令当前位置起的移动距离&#xff0c;进行定位。 指令名称FB/FUN图形表现ST表现MC…

【环境搭建】MySQL安装部署

Win64安装MySQL Windows的玩法比较少&#xff0c;没有像MAC一样给你提供mysqld-safe等等各种的启动脚本&#xff0c;只有手动启动或者是以服务启动Mysql。 点击下载&#xff1a;MySQL5.5-8.0.7z (密码是11) 1.下载软件 这一步下载好软件就可以了&#xff0c;下载地址&#xff…

海山数据库(He3DB)技术分享:He3DB Virtual File Descriptor实现原理

引言 在 He3DB 这样的数据库系统中&#xff0c;文件操作不仅频繁而且复杂。操作系统提供的文件描述符&#xff08;FD&#xff09;数量是有限的&#xff0c;尤其在高并发和大规模数据库操作中&#xff0c;文件描述符资源可能迅速耗尽。为了应对这一挑战&#xff0c;He3DB 引入了…

《深度学习》CNN 数据增强、保存最优模型 实例详解

目录 一、数据增强 1、什么是数据增强 2、目的 3、常用的数据增强方法 4、数据预处理 用法&#xff1a; 5、使用数据增强增加训练数据 二、保存最优模型 1、什么是保存最优模型 2、定义CNN模型 运行结果&#xff1a; 3、设置训练模式 4、设置测试模式、保存最优模…

指针变量的自增、自减运算

指针变量的自增、自减运算相比较于普通变量的自增、自减运算又什么区别呢&#xff1f; 让我们先来复习一下普通变量的自增、自减运算 int main() {int i; //定义一个整型变量printf("请输入一个数字&#xff1a;\n");scanf("%d&qu…

查座位号小程序

如何通过关键词查询信息&#xff1f; 在这个信息化的时代&#xff0c;查座位号小程序为我们提供了一种方便快捷的方式来查询座位信息。无论是在大型会议还是日常办公中&#xff0c;通过小程序快速查找座位号&#xff0c;可以大大提高工作效率。以下是详细的使用指南&#xff0c…

初始MYSQL数据库(7)—— 视图

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a; MYSQL 引言 前面我们学习MySQL数据库时&#xff0c;创建表之后&#xff0c;会在表中插入数据&#xff0c;在需要的时候&#xff0c;也会进行…

stm32 gpio I/O模式以及iic访问

1&#xff0c;硬件补充连接原理图引脚 #define FLASH_BASE ((uint32_t)0x08000000) /*!< FLASH(up to 1 MB) base address in the alias region */ #define CCMDATARAM_BASE ((uint32_t)0x10000000) /*!< CCM(core coupled mem…