java中关于反射与动态代理

java中关于反射与动态代理

java反射技术

1、什么是反射?
Java反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制。所谓的反射,实际上是获取到类的字节码.class文件,再通过Class对象获取类的方法和属性。

反射案例,
反射(Reflection)是 Java 中一种强大的机制,允许在运行时动态地获取类的信息、调用类的方法、操作类的属性等。下面是几个涵盖反射各方面的案例示例:

  1. 获取类的信息
    通过反射可以获取类的信息,如类名、方法、字段等。

import java.lang.reflect.*;public class ReflectionExample {public static void main(String[] args) throws Exception {// 获取类的 Class 对象Class<?> clazz = Class.forName("java.util.ArrayList");// 获取类名String className = clazz.getName();System.out.println("Class Name: " + className);// 获取类的方法信息Method[] methods = clazz.getDeclaredMethods();System.out.println("Methods:");for (Method method : methods) {System.out.println(method.getName());}// 获取类的字段信息Field[] fields = clazz.getDeclaredFields();System.out.println("Fields:");for (Field field : fields) {System.out.println(field.getName());}// 获取类的构造方法信息Constructor<?>[] constructors = clazz.getDeclaredConstructors();System.out.println("Constructors:");for (Constructor<?> constructor : constructors) {System.out.println(constructor.toString());}}
}
  1. 动态创建对象
    通过反射可以动态创建类的对象,并调用其方法。
import java.lang.reflect.*;public class DynamicObjectCreation {public static void main(String[] args) throws Exception {// 获取类的 Class 对象Class<?> clazz = Class.forName("java.util.ArrayList");// 使用反射创建对象Object arrayList = clazz.getDeclaredConstructor().newInstance();// 调用对象的方法Method addMethod = clazz.getDeclaredMethod("add", Object.class);addMethod.invoke(arrayList, "Hello");addMethod.invoke(arrayList, "World");// 输出结果System.out.println(arrayList); // 输出:[Hello, World]}
}
  1. 修改私有字段
    通过反射可以访问和修改类中的私有字段。
import java.lang.reflect.*;public class PrivateFieldAccess {public static void main(String[] args) throws Exception {// 创建一个类的实例MyClass obj = new MyClass();// 获取类的 Class 对象Class<?> clazz = obj.getClass();// 获取私有字段Field privateField = clazz.getDeclaredField("privateField");// 设置字段可访问privateField.setAccessible(true);// 修改私有字段的值privateField.set(obj, "New value");// 访问私有字段的值System.out.println(privateField.get(obj)); // 输出:New value}static class MyClass {private String privateField = "Initial value";}
}
  1. 执行私有方法
    通过反射可以调用类中的私有方法。
java
import java.lang.reflect.*;public class PrivateMethodInvocation {public static void main(String[] args) throws Exception {// 创建一个类的实例MyClass obj = new MyClass();// 获取类的 Class 对象Class<?> clazz = obj.getClass();// 获取私有方法Method privateMethod = clazz.getDeclaredMethod("privateMethod");// 设置方法可访问privateMethod.setAccessible(true);// 调用私有方法privateMethod.invoke(obj);}static class MyClass {private void privateMethod() {System.out.println("Executing private method");}}
}
  1. 使用反射实现通用的 JSON 序列化工具
    通过反射,可以实现一个通用的 JSON 序列化工具,将任意 Java 对象序列化为 JSON 字符串。
java
import com.alibaba.fastjson.JSON;
import java.lang.reflect.*;public class JSONSerializer {public static void main(String[] args) throws Exception {MyClass obj = new MyClass("Alice", 25);// 序列化对象为 JSON 字符串String jsonString = serializeObject(obj);System.out.println(jsonString);// 反序列化 JSON 字符串为对象MyClass newObj = deserializeObject(jsonString, MyClass.class);System.out.println(newObj.getName());System.out.println(newObj.getAge());}// 序列化对象为 JSON 字符串public static String serializeObject(Object obj) {return JSON.toJSONString(obj);}// 反序列化 JSON 字符串为对象public static <T> T deserializeObject(String jsonString, Class<T> clazz) {return JSON.parseObject(jsonString, clazz);}static class MyClass {private String name;private int age;public MyClass(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}}
}

这些示例展示了反射在 Java 中的广泛应用,从获取类信息、动态创建对象、修改私有字段和调用私有方法,到实现通用的 JSON 序列化工具。反射使得在编译时未知类的情况下,能够在运行时进行灵活的操作和扩展。

反射不做过多解释

动态代理 这里主要讲JDK的动态代理

JDK动态代理的步骤
步骤一:定义接口
首先,我们定义一个简单的接口UserService,包含两个方法:save和delete。

java
public interface UserService {void save(String username);void delete(String username);
}
步骤二:实现接口
接下来,实现UserService接口的具体类UserServiceImpl,用于实际执行保存和删除用户的操作。java
public class UserServiceImpl implements UserService {@Overridepublic void save(String username) {System.out.println("Saving user: " + username);}@Overridepublic void delete(String username) {System.out.println("Deleting user: " + username);}
}

步骤三:实现InvocationHandler
创建一个实现了InvocationHandler接口的类UserServiceInvocationHandler,在这里我们将为方法调用添加日志记录功能。

java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class UserServiceInvocationHandler implements InvocationHandler {private final UserService target;public UserServiceInvocationHandler(UserService target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before calling method: " + method.getName());Object result = method.invoke(target, args);System.out.println("After calling method: " + method.getName());return result;}
}

步骤四:创建代理对象
在主程序中,我们使用Proxy.newProxyInstance方法创建代理对象。这里将实现UserService接口的代理对象,它将会在每次调用save和delete方法时,自动在控制台输出调用前后的日志信息。

java
import java.lang.reflect.Proxy;public class Main {public static void main(String[] args) {UserService realService = new UserServiceImpl();UserServiceInvocationHandler handler = new UserServiceInvocationHandler(realService);UserService proxyService = (UserService) Proxy.newProxyInstance(Main.class.getClassLoader(),new Class[]{UserService.class},handler);proxyService.save("Alice");proxyService.delete("Bob");}
}

运行结果
当你运行上述代码时,控制台会输出以下信息:

Before calling method: save
Saving user: Alice
After calling method: save
Before calling method: delete
Deleting user: Bob
After calling method: delete

这些日志信息显示了在调用代理对象的save和delete方法时,UserServiceInvocationHandler中的invoke方法分别在方法调用前后被执行,从而实现了日志记录的功能。

总结
这个例子展示了如何使用JDK动态代理为一个实现了接口的类(UserServiceImpl)生成代理对象,并在方法调用前后添加额外的日志记录逻辑。这种方式可以轻松地为现有代码添加各种额外的横切关注点(cross-cutting concerns),如日志记录、性能监控等,而无需修改原始类的代码。

主要步骤

  UserService realService = new UserServiceImpl();UserServiceInvocationHandler handler = new UserServiceInvocationHandler(realService);UserService proxyService = (UserService) Proxy.newProxyInstance(Main.class.getClassLoader(),new Class[]{UserService.class},handler);

我现在的理解是,JDK的动态代理技术会根据我们想要代理的接口去获取需要实现的代理方法从而去构建一个代理类
在代理类中具有和实现这个接口的子类一样的方法,调用newProxyInstance会创建一个这个代理类的对象,而创建这个对象需要三个参数
一、第一个参数 Main.class.getClassLoader() 指定了类加载器,用于加载生成的代理类,一般这个参数用于当前方法所在类的加载器。具体原因我还未搞清楚
二、第二个参数 new Class[]{UserService.class} 是一个 Class 数组,指定了代理类要实现的接口,这里代理类会实现 UserService接口。这个参数可以去放多个接口,意味着可以代理多个接口的方法进行增强前提是所要代理的类需要实现数组中的接口
三、第三个参数是一个 InvocationHandler 对象,用于处理代理对象的方法调用。实际上InvocationHandler 可以采用匿名实现类来实现,起作用主要是其重写的invoke的方法invoke(Object proxy, Method method, Object[] args)
这个方法可以传入一个被代理的类的对象进行原始方法的调用者指定,也就是谁调用后面的method,Method method是用来指定调用的方法这里其实只有创建了代理对象并且使用代理对象去调用方法时才会知道,因为代理对象的方法名字和被代理对象时一致的我猜这里是直接获取的方法名,Object[] args则是用来指定调用方法的入参,调用被代理的对象的方法的入参。
四、生成对象 UserService proxyService = (UserService) Proxy.newProxyInstance();其实这里生成的是代理类的对,只是代理类字节码文件在jvm中不是我们生成的,我们看不到,实际上是代理类对象

简化写法

   public static void main(String[] args) {Star bigStar = new BigStar("鸡哥");Star star = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(), new Class[]{Star.class}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("代理开始");if ("sing".equals(method.getName())) {System.out.println("代理开始唱歌");} else {System.out.println("代理开始跳舞");}Object result = method.invoke(bigStar, args);System.out.println("代理结束");return result;}});star.sing("鸡哥");}

JDK动态代理问题一 为什么只能代理接口的?而不能代理实现类的。

关于JDK的动态代理设计 ,他底层生产的代理类会继承一个proxy类并且实现要代理的接口实现类的接口,如果是代理接抽象类或者类的 就需要去继承这个抽象类的或者类,这样就无法再去继承peoxy类,从而无法实现JDK的动态代理,这是设计问题。

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

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

相关文章

python:sympy 求解一元五次方程式

pip install sympy 或者 本人用的 anaconda 3 自带 sympy 在北大数学训练营&#xff0c;韦东奕 用卡丹公式 巧妙 求解一元五次方程式&#xff1a; \latex $x^510*x^320*x-4 0$ from sympy import *x symbols(x) expr x**5 10*x**3 20*x -4# 用卡丹公式 尝试化简 a sym…

鸿蒙开发工程师面试题-架构篇

1. 假如让你负责鸿蒙App架构设计&#xff0c;你会关注哪些方面&#xff1f; 分层架构设计&#xff1a; 将应用划分为产品定制层、基础特性层和公共能力层&#xff0c;以降低层间依赖性&#xff0c;提升代码可维护性。通过分层架构设计&#xff0c;进一步明确每层的职责和层间交…

【IOS】React Native之HelloWorld

RN搭建开发环境 rvm 安装3.2.2 brew install node18 brew install watchman# 使用nrm工具切换淘宝源 npx nrm use taobao# 如果之后需要切换回官方源可使用 npx nrm use npmnpm install -g yarnbrew install cocoapodsnpm uninstall -g react-native-cli react-native-communi…

<数据集>表情识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;2504张 标注数量(xml文件个数)&#xff1a;2504 标注数量(txt文件个数)&#xff1a;2504 标注类别数&#xff1a;7 标注类别名称&#xff1a;[Neutral, Happy, Angry, Fear, Sad, surprised, Disguist] 序号类别名…

小白学webgl合集-import.meta.url 和 new URL() bug

为什么使用 import.meta.url 和 new URL() 动态路径解析&#xff1a; 在 ESM&#xff08;ECMAScript Modules&#xff09;环境中&#xff0c;import.meta.url 提供了当前模块的完整 URL。结合 new URL()&#xff0c;你可以基于这个 URL 动态解析其他资源的路径。这样可以确保路…

开始Linux之路(暑假提升)

人生得一知己足矣&#xff0c;斯世当以同怀视之。——鲁迅 Linux操作系统简单操作指令 1、ls指令2、pwd命令3、cd指令4、mkdir指令(重要)5、whoami命令6、创建一个普通用户7、重新认识指令8、which指令9、alias命令10、touch指令11、rmdir指令 及 rm指令(重要)12、man指令(重要…

Transformer——多头注意力机制(Pytorch)

1. 原理图 2. 代码 import torch import torch.nn as nnclass Multi_Head_Self_Attention(nn.Module):def __init__(self, embed_size, heads):super(Multi_Head_Self_Attention, self).__init__()self.embed_size embed_sizeself.heads headsself.head_dim embed_size //…

【VSCode】设置背景图片

1.下载background插件&#xff1a;拓展程序→background→install安装→设置&#xff1a; 2.点击在 settings.json 中编辑&#xff1a; 3.将settings.json文件中所有代码注释&#xff0c;添加以下代码&#xff1a; {// 是否开启背景图显示"background.enabled": t…

【Linux杂货铺】1.环境变量

1.环境变量基本概念 环境变量&#xff08; environment variables &#xff09;一般是指在操作系统中用来指定操作系统运行环境的一些参数。如&#xff1a;我们在编写 C / C &#xff0b;代码的时候&#xff0c;在链接的时候&#xff0c;从来不知道我们的所链接的动态静态库在哪…

【Go系列】 Go语言数据结构

承上启下 在上一次的思维碰撞中&#xff0c;我们的小试牛刀是一段温馨的代码小练习——将“Hello World”这个熟悉的问候&#xff0c;替换成了我们自己的名字。是的&#xff0c;你没听错&#xff0c;就是这么简单&#xff01;以我为例&#xff0c;我将“Hello World”轻轻一变&…

算法训练 | 图论Part8 | 拓扑排序、dijkstra(朴素版)

目录 117. 软件构建 拓扑排序法 47. 参加科学大会 dijkstra法 117. 软件构建 题目链接&#xff1a;117. 软件构建 文章讲解&#xff1a;代码随想录 拓扑排序法 代码一&#xff1a;拓扑排序 #include <iostream> #include <vector> #include <queue> …

什么是Web3D?国内有哪些公司可以做?

Web3D 是一种基于网页的三维立体虚拟现实技术。利用计算机图形学、图像处理、人机交互等技术&#xff0c;将现实世界中的物体、场景或概念以三维立体的方式呈现在网页里。Web3D 技术可以让用户在任何时间、任何地点&#xff0c;通过互联网与虚拟世界进行互动&#xff0c;获得身…

模型剪枝介绍

Ref&#xff1a;https://www.cnblogs.com/the-art-of-ai/p/17500399.html 1、背景介绍 深度学习模型在图像识别、自然语言处理、语音识别等领域取得了显著的成果&#xff0c;但是这些模型往往需要大量的计算资源和存储空间。尤其是在移动设备和嵌入式系统等资源受限的环境下&a…

昇思25天学习打卡营第1天|初步了解

1在昇思平台上申请过相关资源之后&#xff0c;将示例代码粘贴到输入框内。可以在下图中创建一个新的文档。 2不过初次运行的时候会遇到一个问题&#xff0c;点击运行的时候会出现新的输入框&#xff0c;而不是直接运行。遇到此问题等待就可以了&#xff0c;或者稍微等一下再运…

【JVM】对象的生命周期一 | 对象的创建与存储

Java | 对象的生命周期1-对象的创建与存储 文章目录 前言对象的创建过程内存空间的分配方式方式1 | 指针碰撞方式2 | 空闲列表 线程安全问题 | 避免空间冲突的方式方式1 | 同步处理&#xff08;加锁)方式2 | 本地线程分配缓存 对象的内存布局Part1 | 对象头Mark Word类型指针 P…

内网安全:域内信息探测

1.域内基本信息收集 2.NET命令详解 3.内网主要使用的域收集方法 4.查找域控制器的方法 5.查询域内用户的基本信息 6.定位域管 7.powershell命令和定位敏感信息 1.域内基本信息收集&#xff1a; 四种情况&#xff1a; 1.本地用户&#xff1a;user 2.本地管理员用户&#x…

solidity实战练习1

//SPDX-License-Identifier:MIT pragma solidity ^0.8.24; contract PiggyBank{constructor()payable{emit Deposit(msg.value);//触发事件1//意味着在部署合约的时候&#xff0c;可以向合约发送以太币&#xff08;不是通过调用函数&#xff0c;而是直接在部署合约时发送&#…

C++ STL for_each的用法和实现

目录 一&#xff1a;功能 二&#xff1a;用法 三&#xff1a;实现 一&#xff1a;功能 遍历元素 二&#xff1a;用法 //C 11 #include <vector> #include <algorithm> #include <iostream> #include <format>struct StatsFn {int cnt 0;int sum…

外泌体相关基因肝癌临床模型预测——2-3分纯生信文章复现——4.预后相关外泌体基因确定之生存曲线(4)

内容如下: 1.外泌体和肝癌TCGA数据下载 2.数据格式整理 3.差异表达基因筛选 4.预后相关外泌体基因确定 5.拷贝数变异及突变图谱 6.外泌体基因功能注释 7.LASSO回归筛选外泌体预后模型 8.预后模型验证 9.预后模型鲁棒性分析 10.独立预后因素分析及与临床的相关性分析…

【算法】二叉树算法基本概念及实现

目录 一、二叉树的基本概念 二、二叉树的性质 三、二叉树的算法实现 四、二叉树的应用 C# 实现 Python 实现 二叉树算法是计算机科学中常用的一种数据结构算法,主要用于处理具有层级关系的数据。以下是对二叉树算法的详细介绍: 一、二叉树的基本概念 定义:二叉树是n…