【Java】深度解析Java的反射机制

反射(Reflection)

    • 一、 反射的基本概念
    • 二、 获取类的信息
    • 三、 获取类的成员
    • 四、 动态创建对象
    • 五、 动态调用方法
    • 六、 动态访问和修改字段
  • 总结

在这里插入图片描述
在这里插入图片描述

一、 反射的基本概念

反射是一种运行时机制,允许程序在运行时检查和操作类、方法、字段等。通过反射,你可以:

  • 获取类的详细信息(类名、修饰符、父类、接口等)。
  • 获取类的方法、构造函数、字段等。
  • 动态调用方法或构造函数。
  • 动态访问和修改字段的值。

二、 获取类的信息

获取 Class 对象
有多种方法可以获取一个类的 Class 对象:

  1. Class.forName(String className): 通过类的完全限定名获取 Class 对象。

  2. ClassName.class: 通过类的字面常量获取 Class 对象。

  3. object.getClass(): 通过对象实例获取 Class 对象。

// 获取 Class 对象的三种方式
Class<?> clazz1 = Class.forName("java.util.ArrayList");Class<?> clazz2 = ArrayList.class;
ArrayList<String> list = new ArrayList<>();Class<?> clazz3 = list.getClass(); 

三、 获取类的成员

  • getDeclaredFields(): 获取类的所有字段(包括私有字段)。

  • getDeclaredMethods(): 获取类的所有方法(包括私有方法)。

  • getDeclaredConstructors(): 获取类的所有构造函数。

  • getField(String name): 获取类的指定字段(不包括私有字段)。

  • getMethod(String name, Class<?>… parameterTypes): 获取类的指定方法(不包括私有方法)。

  • getConstructor(Class<?>… parameterTypes): 获取类的指定构造函数。

Class<?> clazz = Class.forName("java.util.ArrayList");// 获取所有声明的字段
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {System.out.println("字段: " + field.getName());
}// 获取所有声明的方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {System.out.println("方法: " + method.getName());
}// 获取所有声明的构造函数
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {System.out.println("构造函数: " + constructor.getName());
}

四、 动态创建对象

newInstance(): 使用无参构造函数创建对象。

Constructor.newInstance(Object… initargs): 使用指定构造函数创建对象。

Class<?> clazz = Class.forName("java.util.ArrayList");// 使用无参构造函数创建对象
Object obj1 = clazz.newInstance();// 使用带参数的构造函数创建对象
Constructor<?> constructor = clazz.getConstructor(Collection.class);
Collection<String> collection = Arrays.asList("A", "B", "C");
Object obj2 = constructor.newInstance(collection);System.out.println(obj1);
System.out.println(obj2);

五、 动态调用方法

Method.invoke(Object obj, Object… args): 调用指定对象的该方法。

Class<?> clazz = Class.forName("java.util.ArrayList");
Method method = clazz.getMethod("add", Object.class);ArrayList<String> list = new ArrayList<>();
method.invoke(list, "Hello");
System.out.println(list); // 输出: [Hello]

六、 动态访问和修改字段

Field.get(Object obj): 获取指定对象中此字段的值。

Field.set(Object obj, Object value): 设置指定对象中此字段的值。

 
class MyClass {private String field = "Initial Value";
}Class<?> clazz = Class.forName("MyClass");
Field field = clazz.getDeclaredField("field");
field.setAccessible(true); // 如果字段是私有的,需要设置可访问MyClass obj = new MyClass();
System.out.println("原始字段值: " + field.get(obj)); // 获取字段值field.set(obj, "New Value"); // 设置字段值
System.out.println("修改后的字段值: " + field.get(obj)); // 获取字段值

结合注解与反射

注解与反射的结合非常常见,尤其在框架中,例如 Spring 和 Hibernate。通过反射机制,你可以在运行时读取注解信息,并根据这些信息执行特定的操作。

示例:简单的依赖注入
以下示例展示了如何通过注解和反射实现简单的依赖注入:

import java.lang.annotation.*;
import java.lang.reflect.*;// 定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Inject {
}// 服务类
class Service {public void serve() {System.out.println("Service is serving");}
}// 客户端类
class Client {@Injectprivate Service service;public void doSomething() {service.serve();}
}// 注入依赖的工具类
public class DependencyInjector {public static void main(String[] args) throws Exception {Client client = new Client();injectDependencies(client);client.doSomething();}public static void injectDependencies(Object obj) throws Exception {Class<?> clazz = obj.getClass();for (Field field : clazz.getDeclaredFields()) {	// 遍历client的所有字段(变量).if (field.isAnnotationPresent(Inject.class)) {	// 获取带有Inject注解的变量, 把它注入到 Client 中field.setAccessible(true);Object dependency = field.getType().getConstructor().newInstance();field.set(obj, dependency);	// 通过field的set方法将service实例注入到client中}}}
}

在这个示例中:

@Inject 注解用于标注需要注入的字段。

DependencyInjector 类通过反射获取 Client 类中带有 @Inject 注解的字段,并动态实例化 Service 类的对象,注入到 Client 类的实例中。

Client 类调用 doSomething 方法时,Service 类的实例已经被注入并可以使用。

总结

反射是 Java 中非常强大和灵活的机制,通过它们可以实现许多高级功能,例如依赖注入、AOP、动态代理等。在实际开发中,理解和熟练运用这些技术,可以帮助你编写出更加灵活、可扩展的代码。

文章到这里就这束了!~

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

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

相关文章

JavaFX布局-TabPane

JavaFX布局-TabPane 常用属性paddingsidetabClosingPolicytabDragPolicy 实现方式Java实现fxml实现 组织一组tab的容器&#xff0c;可以设置关闭&#xff0c;拖拽等每个tab内容可以设置不同容器数据 常用属性 padding 内边距&#xff0c;可以单独设置上、下、左、右的内边距 …

docker部署java项目(war包方式)

场景描述:java项目war包,在开发开电脑上使用dockerfile构建镜像,上传镜像到客户服务器中使用docker加载docker镜像,然后部署。 目录 一、本地环境安装 docker git 二、服务器环境安装 docker 三、构建docker镜像(win系统) 四、注意事项 (1)系统架构 (2)使…

如何准备专利申请书的摘要部分?

如何准备专利申请书的摘要部分&#xff1f;

【EI会议征稿通知】第五届大数据、人工智能与软件工程国际研讨会(ICBASE 2024)

重要信息 会议官网&#xff1a;www.icbase.org&#xff08;查看详情&#xff09; 中文主页&#xff1a;【往届会后3个月检索】第五届大数据、人工智能与软件工程国际研讨会&#xff08;ICBASE 2024&#xff09;_艾思科蓝_学术一站式服务平台 会议时间&#xff1a;2024年9月2…

C++笔记---类和对象(中)

1. 类的默认成员函数 默认成员函数就是用户没有显式实现&#xff0c;编译器会自动生成的成员函数称为默认成员函数。 一个类&#xff0c;我们不写的情况下编译器会默认生成以下6个默认成员函数&#xff0c;分别为&#xff1a;构造函数&#xff0c;析构函数&#xff0c;拷贝构…

C#中的Winform基础

program 每个Windows应用程序都会有一个Program类——程序入口点 [STAThread] ----指示应用程序的COM线程模型是单线程单元&#xff08;如果无此特性&#xff0c;无法工作&#xff09; static voidMain() —— 入口 System.Windows.Forms.Application类提供一系列静态方法和…

Bootstrap框架介绍

1、Bootstrap框架的下载和使用 Bootstrap框架是基于HTML、CSS、JavaScript的CCS/HTML框架,是一种封装好的前端框架。它包括js、css、front字体样式库。该框架下载链接:https://v3.bootcss.com/getting-started/#download,并选择下载源码。 建一个BootstrapDemo文件夹,将js…

GoAccess实战秘籍:从新手到高手,跨越那些“坑”与“惑”!

GoAccess实战秘籍:从新手到高手,跨越那些“坑”与“惑”! 致读者: 点击上方 “雪之梦技术驿站” → 点击右上角“ … ”→ 点选“设为星标★ ” 加上星标,就不会找不到我啦! 偷偷溜进文章的小广告,别害羞,点进去瞅瞅,说不定能发现什么宝藏呢!文末那个也别错过,说不定…

鼻咽癌综述

小罗碎碎念 本期推文主题&#xff1a;鼻咽癌综述 这篇文章提供了一个全面的综述&#xff0c;探讨了鼻咽癌&#xff08;NPC&#xff09;的关键研究进展&#xff0c;包括病理机制、治疗、筛查和生物标志物的发展。 文章首先强调了NPC在特定地理区域的流行情况&#xff0c;并讨论了…

nginx反向代理和负载均衡+安装jdk-22.0.2

ps -aux|grep nginx //查看进程 nginx 代理 nginx代理是负载均衡的基础 主机&#xff1a;192.168.118.60 这台主机只发布了web服务&#xff0c;没有做代理的任何操作 修改一下index.html中的内容 echo "this is java web server" > /usr/local/nginx/htm…

Java游戏源码:象棋网络对战版

学习java朋友们&#xff0c;福利来了&#xff0c;今天小编给大家带来了一款象棋网络对战版源码。 源码搭建和讲解 源码分为客户端和服务器&#xff0c;采用java原生 java.net.Socket 实现&#xff0c;服务器主循环代码&#xff1a; import java.net.ServerSocket; import jav…

【吊打面试官系列-Dubbo面试题】Dubbo 配置文件是如何加载到 Spring 中的 ?

大家好&#xff0c;我是锋哥。今天分享关于 【Dubbo 配置文件是如何加载到 Spring 中的 &#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; Dubbo 配置文件是如何加载到 Spring 中的 &#xff1f; Spring 容器在启动的时候&#xff0c;会读取到 Spring 默认的一些…

nodejs多版本随心切换-windows

nodejs多版本控制 1. 安装 nvm github下载地址 不需要卸载已安装的nodejs&#xff0c;安装时会让你选择nodejs的位置&#xff0c;可修改为你已经安装的路径&#xff0c;会自动搜索已安装版本&#xff0c;并进行弹窗询问&#xff0c;选择托管即可 2. 修改配置文件 在 nvm 安装…

案例分享|Alluxio在自动驾驶数据闭环中的应用

分享嘉宾&#xff1a; 孙涛 - 中汽创智智驾工具链数据平台开发专家 关于中汽创智&#xff1a; 中汽创智科技有限公司&#xff08;以下简称“中汽创智”&#xff09;由中国一汽、东风公司、南方工业集团、长安汽车和南京江宁经开科技共同出资设立。聚焦智能底盘、新能动力、智…

K8S可视化管理平台KubeSphere

什么是 KubeSphere &#xff1f; KubeSphere 是一款开源项目&#xff0c;在目前主流容器调度平台 Kubernetes 之上构建的企业级分布式多租户容器管理平台&#xff0c;提供简单易用的操作界面以及向导式操作方式&#xff0c;在降低用户使用容器调度平台学习成本的同时&#xff…

7.怎么配置一个axios来拦截前后端请求

首先创建一个axios.js文件 导入我们所需要的依赖 import axios from "axios"; import Element from element-ui import router from "./router"; 设置请求头和它的类型和地址 注意先注释这个url,还没有解决跨域问题,不然会出现跨域 // axios.defaults.…

计算机网络-http协议和https的加密原理

HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是用于在万维网&#xff08;World Wide Web&#xff09;上传输超文本的基础协议。它定义了客户端&#xff08;通常是浏览器&#xff09;和服务器之间的文本数据传输格式和规则。以下是HTTP的…

假如家里太大了,wifi连不上了怎么办

最近有个土豪朋友抱怨&#xff0c;他家里太大了&#xff0c;一个路由器的Wi-Fi信号根本无法覆盖他们家的每个房间&#xff0c;都没办法上网看奥运会比赛了。&#xff08;还好我是穷人&#xff0c;就没有这种烦恼T_T&#xff09;。 然后我问他为何不用一个路由器作主路由器&…

BGP对等体组、聚合、路由反射器、联盟、团体属性

一.实验拓扑 二.实验需求 1.AS1中存在两个环回&#xff0c;一个地址为192.168.1.0/24&#xff0c;该地址不能在任何协议中宣告 As3中存在两个环回&#xff0c;一个地址为192.168.2.0/24,、该地址不能在任何协议中宣告&#xff0c;最终要求这两个环回可以ping通; 2.整个AS2的I…

vue el-input 输入框下拉显示匹配数据

1、效果图&#xff1a; 2、需求&实现&#xff1a; 输入条件 下面匹配查询到的数据有多少个 需要调用后端接口展示&#xff0c;后端查询到之后返回条数 前端展示 3、具体代码实现&#xff1a; html&#xff1a; 图片需要自己根据实际情况增加 // 查询 重置 筛选 本文章…