Java 中反射的高级用法:窥探 Java 世界的魔法之门

反射(Reflection),这个在 Java 编程语言中既神秘又强大的特性,几乎是每个 Java 程序员都会接触到的工具。但你知道吗?它不只是让你能在运行时查找类、方法和字段的类型信息——它更像是一把钥匙,能打开许多原本无法触及的领域。

那么,今天我们就来深挖一下反射的高级用法。准备好了吗?让我们一起揭开这层神秘的面纱,看看反射如何以出乎意料的方式发挥巨大作用。

1. 动态代理:超越继承的神奇力量

想要编写灵活的代码,而不被死板的继承体系限制?反射提供的动态代理技术,简直是为解锁这一需求而生。想象一下,你可以在运行时创建一个代理类,并根据需要为其指定方法的实现,而无需写一行额外的代码。

示例:创建一个动态代理

假设你有一个简单的接口 Service

public interface Service {void execute();
}

你可以通过反射来动态地创建一个实现了这个接口的代理类:

import java.lang.reflect.*;public class ProxyExample {public static void main(String[] args) {Service originalService = new ServiceImpl();Service proxyService = (Service) Proxy.newProxyInstance(Service.class.getClassLoader(),new Class[] { Service.class },new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method " + method.getName());Object result = method.invoke(originalService, args);System.out.println("After method " + method.getName());return result;}});proxyService.execute();}
}

在这个例子中,Proxy.newProxyInstance() 就像是一台魔法机器,能够在运行时创建一个新的类,自动实现 Service 接口,并在每个方法调用前后加入我们自定义的逻辑。就像是给每个方法都套上了一层透明的外壳,既保留了原有功能,又能增加额外行为。

2. 利用反射注入依赖:Spring 也得敬畏三分

在 Spring 这些大框架中,依赖注入(DI)是一项基础功能。然而,你知道吗?使用反射也能实现类似的效果!通过反射,你可以手动“注入”对象,构建更加灵活的框架,甚至替代 Spring 进行一些简单的 DI 操作。

示例:手动依赖注入

import java.lang.reflect.*;public class ReflectiveDI {public static void main(String[] args) throws Exception {MyClass myClass = new MyClass();Field field = MyClass.class.getDeclaredField("service");field.setAccessible(true);field.set(myClass, new ServiceImpl());myClass.executeService();}
}class MyClass {private Service service;public void executeService() {service.execute();}
}

在上面的例子中,反射让我们直接访问和修改 MyClass 中的 private 字段 service。这种通过反射手动注入依赖的方式,通常用于框架底层的实现,能够极大提升代码的可扩展性。

3. 调用私有方法:打破封装的禁忌

封装是面向对象编程的基石,但有时候,你可能需要“打破”这个原则来访问那些私有方法。这时,反射就成了你的“秘密武器”。通过反射,你可以在运行时访问类的私有成员,甚至是私有方法。

示例:调用私有方法

import java.lang.reflect.*;public class PrivateMethodInvoker {public static void main(String[] args) throws Exception {MyClass myClass = new MyClass();Method privateMethod = MyClass.class.getDeclaredMethod("privateMethod");privateMethod.setAccessible(true);privateMethod.invoke(myClass);}
}class MyClass {private void privateMethod() {System.out.println("Private method invoked!");}
}

在这个例子中,反射突破了 private 访问限制,通过 setAccessible(true),我们让私有方法变得可以访问。尽管这种做法可能会引发设计上的争议,但在一些特定场合(例如单元测试、调试)中,它无疑是一个非常强大的工具。

4. 动态加载类:运行时的类加载大师

反射最令人兴奋的一点,是它能在运行时加载类并进行操作。你可以根据外部配置文件或网络数据来决定加载哪个类,或者根据用户输入的内容动态地选择使用哪个实现。这种灵活性是静态编程难以比拟的。

示例:根据用户输入动态加载类

public class DynamicClassLoader {public static void main(String[] args) throws Exception {String className = "com.example.MyClass";  // 假设这个类名是从配置文件中读取的Class<?> clazz = Class.forName(className);Object obj = clazz.getDeclaredConstructor().newInstance();System.out.println("Class loaded: " + obj.getClass().getName());}
}

这里,Class.forName() 方法根据运行时给定的类名动态加载类,并通过反射实例化对象。你甚至可以动态调用这个类的任何方法,只要你知道它的名字和参数。

5. 获取泛型类型:让类型擦除退散无踪

Java 的泛型是通过类型擦除实现的,这意味着在运行时,你无法直接获得泛型的实际类型信息。然而,反射为我们提供了获取泛型类型的通道,让我们能够绕过这一限制。

示例:获取泛型类型信息

import java.lang.reflect.*;public class GenericTypeInfo {public static void main(String[] args) throws Exception {Field field = MyClass.class.getDeclaredField("list");ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();Type actualTypeArgument = parameterizedType.getActualTypeArguments()[0];System.out.println("Generic type: " + actualTypeArgument.getTypeName());}
}class MyClass {private List<String> list;
}

在这个例子中,getGenericType() 让我们能够获取字段的泛型类型信息。通过 ParameterizedType,我们成功地抓住了擦除后的泛型类型(String)。

6. 枚举类型反射:探索枚举类背后的秘密

最后,反射在枚举类中的应用也是一项不容忽视的高级技巧。你可以通过反射获取枚举的所有常量,甚至能调用它们的私有构造方法。这种技巧对构建更加动态和灵活的代码有很大帮助。

示例:枚举类型反射

import java.lang.reflect.*;public class EnumReflection {public static void main(String[] args) throws Exception {Class<?> clazz = Class.forName("com.example.MyEnum");Object[] enumConstants = clazz.getEnumConstants();for (Object constant : enumConstants) {System.out.println(constant);}}
}enum MyEnum {CONSTANT_ONE,CONSTANT_TWO;
}

在这个例子中,我们通过反射获取 MyEnum 的所有常量并打印出来,完全无需显式地列出它们。

结语:反射的魔法与理性

反射,作为一种强大的工具,确实能够为我们打开一些非常方便的大门,但它也有其使用的边界。过度使用反射会导致性能下降,增加代码复杂性,甚至使得代码更加难以维护。因此,掌握它的高阶技巧固然令人激动,但理性地使用它才是我们作为开发者应该追求的目标。

这就是反射的高级用法,背后有着强大的潜力,而掌握这些潜力,将使你在 Java 世界中行走如风,游刃有余。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue中proxy代理配置(测试一)

接口地址&#xff1a;http://jsonplaceholder.typicode.com/posts 1、配置一&#xff08;代理没起作用&#xff09; &#xff08;1&#xff09;设置baseURL为http://jsonplaceholder.typicode.com &#xff08;2&#xff09;proxy为 ‘/api’&#xff1a;’ ’ &#xff08;3&a…

Element-ui的使用教程 基于HBuilder X

文章目录 1.Element-ui简介2.使用HBuilderX 创建一个基于Vue3的项目 &#xff08;由于是使用的基于Vue3的Element-ui&#xff09;3.安装element-ui4.在项目里完全引用element-ui5.引用组件6.运行项目 1.Element-ui简介 Element&#xff0c;一套为开发者、设计师和产品经理准备…

C语言从入门到放弃教程

C语言从入门到放弃 1. 介绍1.1 特点1.2 历史与发展1.3 应用领域 2. 安装2.1 编译器安装2.2 编辑器安装 3. 第一个程序1. 包含头文件2. 主函数定义3. 打印语句4. 返回值 4. 基础语法4.1 注释4.1.1 单行注释4.1.2 多行注释 4.2 关键字4.2.1 C语言标准4.2.2 C89/C90关键字&#xf…

Python OCR 文字识别

一.引言 文字识别&#xff0c;也称为光学字符识别&#xff08;Optical Character Recognition, OCR&#xff09;&#xff0c;是一种将不同形式的文档&#xff08;如扫描的纸质文档、PDF文件或数字相机拍摄的图片&#xff09;中的文字转换成可编辑和可搜索的数据的技术。随着技…

LeetCode:257. 二叉树的所有路径

跟着carl学算法&#xff0c;本系列博客仅做个人记录&#xff0c;建议大家都去看carl本人的博客&#xff0c;写的真的很好的&#xff01; 代码随想录 LeetCode&#xff1a;257. 二叉树的所有路径 给你一个二叉树的根节点 root &#xff0c;按 任意顺序 &#xff0c;返回所有从根…

C++----------类的设计

二维点的表示&#xff08;类设计&#xff09; 知识点讲解&#xff1a; 封装&#xff1a;将数据成员&#xff08;x和y坐标&#xff09;设为private&#xff0c;这遵循了面向对象编程中的封装原则&#xff0c;防止外部代码随意修改类内部的数据&#xff0c;保证数据的安全性和完整…

教育行业 UI 设计基础篇:简洁直观的风格打造

在当今数字化时代&#xff0c;教育行业的线上平台如雨后春笋般涌现&#xff0c;而 UI 设计作为用户与教育产品交互的重要桥梁&#xff0c;其重要性不言而喻。对于教育行业而言&#xff0c;简洁直观的 UI 风格能够极大地提升用户体验&#xff0c;帮助学习者更高效地获取知识。 …

分布式协同 - 分布式事务_2PC 3PC解决方案

文章目录 导图Pre2PC&#xff08;Two-Phase Commit&#xff09;协议准备阶段提交阶段情况 1&#xff1a;只要有一个事务参与者反馈未就绪&#xff08;no ready&#xff09;&#xff0c;事务协调者就会回滚事务情况 2&#xff1a;当所有事务参与者均反馈就绪&#xff08;ready&a…

GFPS扩展技术原理(七)-音频切换消息流

音频切换消息流 Seeker和Provider通过消息流来同步音频切换能力&#xff0c;触发连接做切换&#xff0c;获取或设置音频切换偏好&#xff0c;通知连接状态等等。为此专门定义了音频切换消息流Message Group 为0x07&#xff0c;Message codes如下&#xff1a; MAC of Audio s…

实现 QTreeWidget 中子节点勾选状态的递归更新功能只影响跟节点的状态父节点状态不受影响

在 Qt 开发中&#xff0c;QTreeWidget 提供了树形结构的显示和交互功能。为了实现某个子节点勾选或取消勾选时&#xff0c;只影响当前节点及其子节点的状态&#xff0c;同时递归更新父节点的状态以正确显示 Qt::PartiallyChecked 或 Qt::Checked&#xff0c;我们可以借助 Qt 的…

计算机图形学知识点汇总

一、计算机图形学定义与内容 1.图形 图形分为“图”和“形”两部分。 其中&#xff0c;“形”指形体或形状&#xff0c;存在于客观世界和虚拟世界&#xff0c;它的本质是“表示”&#xff1b;而图则是包含几何信息与属性信息的点、线等基本图元构成的画面&#xff0c;用于表达…

Yolo11改策略:卷积改进|SAC,提升模型对小目标和遮挡目标的检测性能|即插即用

摘要 一、论文介绍 本文参考的论文主要介绍了DetectoRS模型&#xff0c;一个高性能的目标检测模型。DetectoRS通过引入递归特征金字塔&#xff08;RFP&#xff09;和可切换空洞卷积&#xff08;SAC&#xff09;两大创新点&#xff0c;显著提升了目标检测的精度。尽管原文并未…

Y3编辑器教程8:资源管理器与存档、防作弊设置

文章目录 一、资源管理器简介1.1 界面介绍1.2 资源商店1.3 AI专区1.3.1 AI文生图1.3.2 AI图生图1.3.3 立绘头像 二、导入导出2.1 文件格式2.2 模型导入2.2.1 模型制作后导出2.2.2 模型文件导入Y3编辑器2.2.3 Y3编辑器角色、装饰物模型要求 2.3 纹理导入2.4 材质贴图2.4.1 材质支…

【联动】【MSS】【AF】

【联动】【MSS】【AF】 一、版本要求 AF&#xff1a;不低于8.0.7&#xff1b;AF8.0.7R2不支持接入 二、接入配置 2.1、AF配置 对于AF8.0.13版本及以上&#xff0c;登录WEB控制台&#xff0c;点击[下一代安全防护体系] -> [云网联动] -> [云网接入设置]&#xff0c;然…

攻防世界 cookie

开启场景 Cookie&#xff08;HTTP cookie&#xff09;是一种存储在用户计算机上的小型文本文件。它由网站通过用户的浏览器在用户访问网站时创建&#xff0c;并存储一些用于跟踪和识别用户的信息。Cookie 主要用于在网站和浏览器之间传递数据&#xff0c;以便网站可以根据用户的…

STM32-笔记11-手写带操作系统的延时函数

1、为什么带操作系统的延时函数&#xff0c;和笔记10上的延时函数不能使用同一种&#xff1f; 因为笔记10的延时函数在每次调用的时候&#xff0c;会一直开关定时器&#xff0c;而在FreeRTOS操作系统中&#xff0c;SysTick定时器当作时基使用。 时基是一个时间显示的基本单位。…

JWT令牌与微服务

1. 什么是JWT JWT&#xff08;JSON Web Token&#xff09;是一种开放标准(RFC 7519)&#xff0c;它定义了一种紧凑且自包含的方式&#xff0c;用于作为JSON对象在各方之间安全地传输信息。JWT通常用于身份验证和信息交换。 以下是JWT的一些关键特性&#xff1a; 紧凑&#xff…

代码随想录Day37 动态规划:完全背包理论基础,518.零钱兑换II,本周小结动态规划,377. 组合总和 Ⅳ,70. 爬楼梯(进阶版)。

1.完全背包理论基础 思路 完全背包 有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品都有无限个&#xff08;也就是可以放入背包多次&#xff09;&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 完…

【多时段】含sop的配电网重构【含分布式电源】【已更新视频讲解】

1 主要内容 之前分享了很多配电网重构的程序&#xff0c;每个程序针对场景限定性比较大&#xff0c;程序初学者修改起来难度较大&#xff0c;本次分享一个基础程序&#xff0c;针对含sop的配电网重构模型&#xff0c;含风电和光伏&#xff0c;优化了33节点网络电压合理性&…

查看php已安装扩展命令

在powershell中查看完整的拓展 php -m 指定搜索某几个拓展 php -m | Select-String -Pattern "xml"