Java--反射

Java反射机制详解

1. 反射的作用

  • 获取任意一个类中的所有信息:通过反射,可以在运行时获取类的所有信息,包括构造方法、成员方法、成员变量等。
  • 结合配置文件动态创建对象:反射可以与配置文件结合,动态地创建对象,实现程序的灵活配置和扩展。

2. 获得 Class 字节码文件对象的三种方法

  • Class.forName("全类名"):通过类的全限定名获取 Class 对象。
  • 类名.class:直接通过类名获取 Class 对象,常用于传递参数。
  • 对象.getClass():通过对象的 getClass() 方法获取对应的 Class 对象。

3. 如何获取构造方法、成员方法、成员变量

关键词说明:

  • get:获取
  • set:设置
  • Constructor:构造方法
  • Parameter:参数
  • Field:成员变量
  • Modifiers:修饰符
  • Method:方法
  • Declared:私有的

一、定义

反射(Reflection)是 Java 语言的一大特色,它允许程序在运行时检查和操作自身的结构。通过反射,程序可以在运行时获取类的字段、方法和构造函数的信息,即使这些成员被封装(如被 private 修饰)也可以访问。

反射的优点主要体现在以下两个方面:

  1. 无视修饰符访问类的成员:反射能够绕过访问修饰符的限制,直接访问类的私有成员。然而,这种操作在日常开发中不推荐使用,一般用于框架底层或特殊需求的场景。

  2. 动态创建对象和调用方法:反射可以与配置文件结合,动态地创建对象和调用方法。这使得程序具有高度的灵活性和扩展性,能够在运行时决定程序的行为。

二、获取 Class 对象的三种方式

要使用反射,首先需要获取类的 Class 对象,主要有以下三种方式:

1. 使用 Class.forName("全类名")

这是最常用的方式,适用于已知类的完整包名和类名的情况。

try {// 通过类的全限定名获取 Class 对象Class<?> clazz = Class.forName("com.example.MyClass");
} catch (ClassNotFoundException e) {e.printStackTrace();
}

2. 通过 类名.class

这种方式主要用于传递 Class 对象作为参数的场景。

// 直接获取 Class 对象
Class<MyClass> clazz = MyClass.class;

3. 通过对象的 getClass() 方法

当已经有类的实例对象时,可以通过该对象获取对应的 Class 对象。

MyClass obj = new MyClass();
// 通过对象获取 Class 对象
Class<? extends MyClass> clazz = obj.getClass();

setAccessible 方法

在反射中,setAccessible(true) 方法用于取消 Java 的访问控制检查,使得私有成员也可以被访问。这被称为暴力反射。在访问私有构造函数、方法或字段之前,需要调用该方法。

Field privateField = clazz.getDeclaredField("privateFieldName");
// 取消访问权限检查
privateField.setAccessible(true);

注意:使用 setAccessible(true) 会带来安全风险,应谨慎使用。

三、获取构造方法对象(Constructor)

通过 Constructor 类,可以获取类的构造方法,并使用它来创建新的对象。

获取构造方法

  • 获取所有公共构造方法:

    Constructor<?>[] constructors = clazz.getConstructors();
    for (Constructor<?> constructor : constructors) {System.out.println("公共构造方法:" + constructor);
    }
    
  • 获取所有构造方法(包括私有、保护、默认、公有):

    Constructor<?>[] constructors = clazz.getDeclaredConstructors();
    for (Constructor<?> constructor : constructors) {System.out.println("所有构造方法:" + constructor);
    }
    
  • 获取指定参数的公共构造方法:

    try {Constructor<MyClass> constructor = clazz.getConstructor(String.class, int.class);System.out.println("指定的公共构造方法:" + constructor);
    } catch (NoSuchMethodException e) {e.printStackTrace();
    }
    
  • 获取指定参数的构造方法(包括私有):

    try {Constructor<MyClass> constructor = clazz.getDeclaredConstructor(String.class);System.out.println("指定的构造方法:" + constructor);
    } catch (NoSuchMethodException e) {e.printStackTrace();
    }
    

使用构造方法创建对象

  • 创建新实例:

    try {// 假设有一个带两个参数的构造方法Constructor<MyClass> constructor = clazz.getConstructor(String.class, int.class);// 创建对象MyClass instance = constructor.newInstance("John", 25);
    } catch (InstantiationException | IllegalAccessException |IllegalArgumentException | InvocationTargetException |NoSuchMethodException e) {e.printStackTrace();
    }
    
  • 取消访问检查(暴力反射):

    try {// 获取私有构造方法Constructor<MyClass> privateConstructor = clazz.getDeclaredConstructor();// 取消访问权限检查privateConstructor.setAccessible(true);// 创建对象MyClass instance = privateConstructor.newInstance();
    } catch (Exception e) {e.printStackTrace();
    }
    

四、字段(Field)的操作

通过 Field 类,可以访问类的成员变量。

获取字段信息

  • 获取所有公共字段:

    Field[] fields = clazz.getFields();
    for (Field field : fields) {System.out.println("公共字段:" + field.getName());
    }
    
  • 获取所有字段(包括私有、保护、默认、公有):

    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {System.out.println("所有字段:" + field.getName());
    }
    
  • 获取指定名称的公共字段:

    try {Field field = clazz.getField("publicFieldName");System.out.println("指定的公共字段:" + field.getName());
    } catch (NoSuchFieldException e) {e.printStackTrace();
    }
    
  • 获取指定名称的字段(包括私有):

    try {Field field = clazz.getDeclaredField("fieldName");System.out.println("指定的字段:" + field.getName());
    } catch (NoSuchFieldException e) {e.printStackTrace();
    }
    

操作字段的值

  • 设置字段的值:

    try {// 获取字段Field field = clazz.getDeclaredField("name");// 取消访问权限检查field.setAccessible(true);// 创建对象MyClass instance = clazz.newInstance();// 设置字段的值field.set(instance, "Alice");System.out.println("字段值已设置:" + field.get(instance));
    } catch (Exception e) {e.printStackTrace();
    }
    
  • 获取字段的值:

    try {Field field = clazz.getDeclaredField("age");field.setAccessible(true);MyClass instance = clazz.newInstance();int age = (int) field.get(instance);System.out.println("字段值:" + age);
    } catch (Exception e) {e.printStackTrace();
    }
    

五、方法(Method)的操作

通过 Method 类,可以获取类的方法,并在运行时调用。

获取方法信息

  • 获取所有公共方法(包括继承的):

    Method[] methods = clazz.getMethods();
    for (Method method : methods) {System.out.println("公共方法:" + method.getName());
    }
    
  • 获取所有方法(包括私有、保护、默认、公有,不包括继承的):

    Method[] methods = clazz.getDeclaredMethods();
    for (Method method : methods) {System.out.println("所有方法:" + method.getName());
    }
    
  • 获取指定名称和参数类型的公共方法:

    try {Method method = clazz.getMethod("publicMethodName", 参数类型列表);System.out.println("指定的公共方法:" + method.getName());
    } catch (NoSuchMethodException e) {e.printStackTrace();
    }
    
  • 获取指定名称和参数类型的方法(包括私有):

    try {Method method = clazz.getDeclaredMethod("methodName", 参数类型列表);System.out.println("指定的方法:" + method.getName());
    } catch (NoSuchMethodException e) {e.printStackTrace();
    }
    

调用方法

  • 调用实例方法:

    try {// 获取方法Method method = clazz.getDeclaredMethod("setName", String.class);// 取消访问权限检查method.setAccessible(true);// 创建对象MyClass instance = clazz.newInstance();// 调用方法method.invoke(instance, "Bob");// 验证结果System.out.println("姓名已设置为:" + instance.getName());
    } catch (Exception e) {e.printStackTrace();
    }
    
  • 调用静态方法:

    try {Method method = clazz.getDeclaredMethod("staticMethod");method.setAccessible(true);// 调用静态方法,不需要实例对象,传入 nullmethod.invoke(null);
    } catch (Exception e) {e.printStackTrace();
    }
    

示例类:

public class MyClass {private String name;private int age;// 私有构造方法private MyClass() {this.name = "Default";this.age = 0;}// 公共构造方法public MyClass(String name, int age) {this.name = name;this.age = age;}// 私有方法private void setName(String name) {this.name = name;}// 公共方法public String getName() {return name;}// 静态方法private static void staticMethod() {System.out.println("调用了静态方法!");}
}

六、总结

反射是 Java 提供的一种强大机制,允许程序在运行时检查和操作对象的内部信息。它为框架和库的开发提供了极大的灵活性,但也带来了性能和安全方面的考虑。

使用反射的注意事项:

  • 性能影响:反射涉及大量的动态类型检查,可能会降低程序性能。在性能敏感的场景下,应尽量避免频繁使用反射。

  • 安全风险:反射可以绕过访问修饰符,访问私有成员,可能会破坏类的封装性,带来安全隐患。

  • 可维护性:过度使用反射会使代码难以阅读和维护,应在必要时使用。

反射的应用场景:

  • 框架开发:如 Spring、Hibernate 等框架大量使用反射来实现依赖注入、对象持久化等功能。

  • 通用工具类:如通用的对象拷贝、序列化和反序列化工具。

  • 测试工具:在单元测试中,可以使用反射来测试类的私有方法和字段。


希望这篇文章能帮助你更好地理解 Java 的反射机制。如有疑问,欢迎在评论区交流!

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

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

相关文章

JVM面试题总结

1.介绍一下JVM的内存结构 JDK1.8及以后&#xff0c;JVM主要分为元空间、堆、虚拟机栈、本地方法栈、程序计数器五个部分&#xff0c;另外还有一个直接内存部分&#xff0c;是直接属于操作系统的。 其中元空间、堆是线程共享的&#xff0c;虚拟机栈、本地方法栈、程序计数器是线…

小新Pro 14 AHP9 2024款(83D3)原装oem预装系统Win11恢复安装包下载

适用品牌机型 &#xff1a;LENOVO联想【83D3】 链接&#xff1a;https://pan.baidu.com/s/10RAxNdvYPWJ21b_4--Y7Xw?pwdo5ju 提取码&#xff1a;o5ju 联想原装出厂Windows11系统自带所有驱动、出厂主题壁纸、系统属性联机支持标志、系统属性专属LOGO标志、Office365办公软…

【论文笔记】Towards Privacy-Aware Sign Language Translation at Scale

&#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平。 基本信息 标题: Towards Privacy-Aware Si…

Spring:bean的配置

对于bean的配置中&#xff0c;主要会讲解bean基础配置,bean的别名配置,bean的作用范围配置(重点),这三部分内容&#xff1a; bean基础配置 id与class配置 bean的name属性 bean的别名配置 bean作用范围scope配置 scope使用后续思考 介绍完scope属性以后&#xff0c;我们…

贴代码框架PasteForm特性介绍之markdown和richtext

简介 PasteForm是贴代码推出的 “新一代CRUD” &#xff0c;基于ABPvNext&#xff0c;目的是通过对Dto的特性的标注&#xff0c;从而实现管理端的统一UI&#xff0c;借助于配套的PasteBuilder代码生成器&#xff0c;你可以快速的为自己的项目构建后台管理端&#xff01;目前管…

【RK3588 Linux 5.x 内核编程】-内核中的链表(Linked List)及使用

内核中的链表(Linked List)及使用 文章目录 内核中的链表(Linked List)及使用1、Linked List介绍2、Linux内核中的链表3、链表的操作3.1链表初始化3.2 创建节点3.3 添加节点3.4 删除节点3.5 替换节点3.6 移动节点3.7 链表旋转3.8 链表检测3.9 链表分割与合并3.10 链表遍历4、驱…

永夜星河主题特效2(星河背景 + 闪烁文字+点击星星 + 文字弹出特效)

目录 图片展示 星河背景 闪烁文字点击星星 文字弹出特效 特效介绍&#xff1a; 使用方式&#xff1a; 图片展示 星河背景 闪烁文字点击星星 文字弹出特效 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8">&l…

通过JS实现下载图片到本地教程分享

今天分享的这个方法我之前自己试了一下&#xff0c;感觉还行&#xff0c;原理就是通过<a>标签的新增属性实现的&#xff0c;然后可以强制触发下载功能&#xff0c;废话不多说&#xff0c;直接上教程。 首先在HTML写下面的代码: <a href"img.jpg" download…

Harmony错题本--@Preview标注上依然无法预览

初学HarmonyOs开发&#xff0c;写了一个超级简单的组件&#xff0c;但是代码上没有什么问题&#xff0c;DevEco Studio却无法完成预览 代码如下&#xff1a; // 单纯的右键-> ArkTsFile的话&#xff0c;可以创建一个组件。 // 原因是&#xff0c;之前我们学过通过右键->…

【linux学习指南】VSCode部署Ubantu云服务器,与Xshell进行本地通信文件编写

文章目录 &#x1f4dd;前言&#x1f320; 步骤&#x1f309;测试同步 &#x1f6a9;总结 &#x1f4dd;前言 本文目的是讲使用Vscode连接Ubantu,与本地Xshell建立通信同步文件编写。 查看本机系统相关信息&#xff1a; cat /etc/lsb*DISTRIB_IDUbuntu: 表示这是 Ubuntu 发行…

ES-针对某个字段去重后-获取某个字段值的所有值

针对上面表的数据&#xff0c;现在想根据age分组&#xff0c;并获取每个分组后的name有哪些(去重后)。 select age, GROUP_CONCAT(DISTINCT(name)) from testtable group by age ; 结果&#xff1a; 如果想要增加排序&#xff1a; SELECT age, GROUP_CONCAT(DISTINCT name)…

基于java+SpringBoot+Vue的在线考试系统设计与实现

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis Maven mysql5.7或8.0等等组成&#x…

一文详细深入总结服务器选型

1. 题记&#xff1a; 服务器选型工作是项目规划检讨的一项非常重要的工作&#xff0c;本文详细深入总结服务器选型。 2. 服务器基础知识概览 2.1 服务器的定义与功能 2.1 .1 定义 服务器是一种高性能计算机&#xff0c;其设计目的是在网络中提供服务。它可以处理来自多个客…

Linux 入门——基本指令1

目录 一背景知识的简介 二 入门相关指令的使用 一.背景知识的简介 1.认识 Linux &#xff0c;了解Linux 的相关背景 其实Linux 是从 Unix 发展而来的。 Linux&#xff0c;一般指GNU/Linux&#xff08;单独的Linux内核并不可直接使用&#xff0c;一般搭配GNU套件&#xff0…

2024年数维杯国际赛赛题浅析-助攻快速选题

本届数维杯我们将选择 MCM的B题以及ICM的D题进行助攻&#xff0c;具体助攻时间轴如下所示 11.15 12&#xff1a;00 更新赛题翻译、相关文献资料、选题建议、赛题难度 11.15 16&#xff1a;00 更新人工精翻版本赛题、数据预处理代码 11.15 24&#xff1a;00 更新完整解题思路…

w038基于SpringBoot的网上租赁系统设计与实现

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0…

windows 安装Ubuntu 后如何使用

windows 安装Ubuntu 后如何使用 youtube链接 https://www.youtube.com/watch?vPaEcQmgEz78哔哩哔哩视频 https://www.bilibili.com/video/BV1tW42197za/?spm_id_from333.999.0.0两个视频是一样的安装Ubuntu 安装docker的教程&#xff0c;不执行docker的安装即可 安装完毕后…

HashMap面试知识点

一、HashMap实现原理 JDK1.7之前&#xff1a;HashMap由数组链表组成。 JDK1.8之后&#xff1a;HashMap由数组链表、红黑树组成&#xff0c;当链表长度超过8&#xff0c;且 二、HashMap中put()方法的过程 ①首先检查数组table是否为空&#xff0c; 为空的话通过resize()方法进…

OceanBase 闪回查询

前言 在OB中&#xff0c;drop表可以通过 回收站 或者 以往的备份恢复来还原单表。当delete数据时&#xff0c;由于delete操作的对象不会进入回收站&#xff0c;此时需要通过闪回查询功能查看delete的数据&#xff0c;以便后续恢复 本次实验版本为 OceanBase 4.2.1.8&#xff0…

[A-18]ARMv8/ARMv9-Memory-内存空间的属性(Attributes Properties)

ver0.1 [看前序文章有惊喜,关注“浩瀚架构师”,可以解锁全部文章] 前言 在宏伟的ARM的内存世界中VMSA中,属性这个议题算不上最亮的星,就和屏幕前的你和我一样,平凡的活在这个茫茫然的人世间。纵使“丈夫贫践应未足,今日相逢无酒钱。”,也不要灰心面对生活,因为“山重…