Java进阶six junit单元测试,反射,注解,动态代理

前言

Java进阶课程的第六篇,也是最后一篇,junit单元测试,反射,注解,动态代理相关内容


包含知识点

 junit单元测试
反射

1.内部类Student:

包含私有/公共字段和方法

包含默认构造器和私有构造器

2.获取Class对象的三种方式:

.class 语法直接获取

通过对象实例的getClass()方法获取

最常用的Class.forName()动态加载方式

3.反射创建对象:

通过无参构造器创建实例

访问私有构造器创建实例(需要设置setAccessible(true))

4.字段操作:

访问公共字段直接修改值

访问私有字段需要设置可访问权限

5.方法调用:

调用公共方法

调用私有方法需要设置可访问权限

演示带参数方法的调用

6.类结构信息获取:

获取类名和方法列表

区分getMethods()和getDeclaredMethods()的区别

7.关键点说明:

setAccessible(true)可以突破私有成员的访问限制,但会破坏封装性

反射操作需要处理各种异常(本示例直接抛出简化代码)

反射的性能较低,适合框架开发等需要高度灵活性的场景

反射可以访问到类的完整结构信息,包括注解、泛型等

注解

1.自定义注解:
使用@interface关键字来定义一个新的注解类型。例如:public @interface MyInterface {}。
注解可以包含成员变量(也称作元素),这些成员变量可以有默认值或必须在使用时提供值。
元注解(Meta-annotations):
@Target指定注解的应用目标(如方法、字段等)。例如:@Target({ElementType.METHOD})表示该注解只能应用于方法上。
@Retention定义了注解的生命周期,RetentionPolicy.RUNTIME表示该注解将在运行时保留,可以通过反射读取。
2.注解的使用:
注解可以直接应用到类、方法或字段上,通过在它们之前加上注解名称并提供必要的参数值。例如:@MyTest(count = 2)。
3.反射与注解:
利用Java的反射机制,可以在运行时检查类、方法或字段上的注解。例如,通过method.isAnnotationPresent(MyTest.class)判断方法是否被特定注解标记,并通过method.getDeclaredAnnotation(MyTest.class)获取注解实例。
反射还可以用于调用带有注解的方法,如示例中method.invoke(ad)执行被@MyTest注解标记的方法。
4.注解属性的默认值:
注解成员可以设置默认值,如果在使用注解时不提供值,则会使用默认值。例如:int count() default 1;。
5.特殊属性value:
如果注解只有一个名为value的成员,在使用注解时可以省略成员名直接提供值。例如:@MyInterface("delete")相当于@MyInterface(value="delete")。

动态代理

1.业务接口 (UserService)

定义代理类和真实类共同遵守的规范

动态代理只能基于接口实现

2.真实对象 (UserServiceImpl)

实际业务逻辑的实现类

包含需要被增强的核心逻辑

3.调用处理器 (LoggingHandler)

实现InvocationHandler接口

持有真实对象引用(目标对象)

invoke()方法中实现统一代理逻辑

通过反射调用真实方法(method.invoke())

可以添加前置/后置增强逻辑(如日志、事务等)

4.代理对象创建 (Proxy.newProxyInstance)

参数1:类加载器(通常使用接口的类加载器)

参数2:代理类需要实现的接口数组

参数3:调用处理器实例

返回实现指定接口的代理对象

5.动态代理特点:

运行时动态生成代理类(通过ProxyGenerator生成)

代理类名通常为$Proxy+数字

继承Proxy类(所以不能代理类,只能代理接口)

实现了指定的业务接口

方法调用会被路由到InvocationHandler

6.典型应用场景:

AOP编程(日志、事务、权限控制)

RPC框架调用

服务接口的监控统计

单元测试Mock对象

7.注意事项:

代理对象的方法调用都会经过invoke方法

在invoke方法中谨慎使用proxy参数,容易引发递归调用

性能相比静态代理略低(反射调用)

无法代理final类和final方法


具体代码

 junit单元测试
package ADV_0;import org.junit.Test;class StringUtil {//业务类的一个方法public static void printNumber(String name){if(name == null){System.out.println("参数为null");return;}System.out.println("名字长度是:" + name.length());}
}// 测试类:junit单元测试框架,对业务类方法进行测试
public class StringUtilTest {// 测试方法:必公开public,无参,无返回值// 测试方法必须加上@Test注解(Junit核心)@Testpublic void testPrint() {StringUtil.printNumber("微光zc"); // 5// 测试用例,测试核心,各种情况下的测试StringUtil.printNumber("");StringUtil.printNumber(null);}
}
反射
package ADV_0;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;/*** Java反射机制示例类* 演示如何通过反射操作类信息、创建对象、访问字段和调用方法*/
public class ADVZc5 {// 示例用内部类static class Student {private String name;public int age;public Student() {this.name = "Default";this.age = 18;}private Student(String name) {this.name = name;this.age = 20;}public void showInfo() {System.out.println("Student: " + name + ", " + age);}private void setAge(int age) {this.age = age;}}public static void main(String[] args) throws Exception {System.out.println("加载类,获取类的字节码:Class对象");System.out.println("获取类的构造器:Constructor对象");System.out.println("获取类的成员变量:Field对象");System.out.println("获取类的成员方法:MMethod对象");// 1. 获取Class对象的三种方式// 方式一:通过类名.class获取Class<Student> clazz1 = Student.class;// 方式二:通过对象.getClass()获取Student student = new Student();Class<? extends Student> clazz2 = student.getClass();// 方式三:通过Class.forName()获取(最常用)Class<?> clazz3 = Class.forName("ADV_0.ADVZc6$Student"); // 内部类需要用$符号System.out.println("三个Class对象是否相同:" + (clazz1 == clazz2 && clazz2 == clazz3));// 2. 通过反射创建对象// 使用无参构造器Constructor<?> constructor1 = clazz1.getDeclaredConstructor();Student s1 = (Student) constructor1.newInstance();s1.showInfo();// 使用私有构造器(需要设置可访问)Constructor<?> constructor2 = clazz1.getDeclaredConstructor(String.class);constructor2.setAccessible(true); // 突破私有访问限制Student s2 = (Student) constructor2.newInstance("Alice");s2.showInfo();// 3. 访问字段// 访问公共字段Field ageField = clazz1.getField("age");ageField.set(s1, 25); // 等价于 s1.age = 25System.out.print("修改公共字段后:");s1.showInfo();// 访问私有字段(需要设置可访问)Field nameField = clazz1.getDeclaredField("name");nameField.setAccessible(true);nameField.set(s1, "Bob"); // 等价于 s1.name = "Bob"System.out.print("修改私有字段后:");s1.showInfo();// 4. 调用方法// 调用公共方法Method showInfoMethod = clazz1.getMethod("showInfo");System.out.print("反射调用方法:");showInfoMethod.invoke(s2);// 调用私有方法(需要设置可访问)Method setAgeMethod = clazz1.getDeclaredMethod("setAge", int.class);setAgeMethod.setAccessible(true);setAgeMethod.invoke(s2, 30);System.out.print("修改私有字段后:");s2.showInfo();// 5. 获取类结构信息System.out.println("\n类结构信息:");System.out.println("类名:" + clazz1.getName());System.out.println("简单类名:" + clazz1.getSimpleName());System.out.println("公共方法列表:");for (Method method : clazz1.getMethods()) {System.out.println("  " + method.getName());}System.out.println("声明字段列表:");for (Field field : clazz1.getDeclaredFields()) {System.out.println("  " + field.getName());}System.out.println("===反射的基本作用===");// 1、类的全部成分的获取// 2、可以破坏封装性// 3、可以绕过泛型的约束System.out.println("===反射的应用:做框架的通用技术===");}
}
注解
package ADV_0;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;public class ADVZc6 {// 自定义注解public @interface MyInterface {String name();int age() default 18;String[] address();}
}@Target({ElementType.METHOD}) // 表示注解的作用目标为方法
@Retention(RetentionPolicy.RUNTIME) // 表示注解的保留策略: 编译器运行时(一直活着)
@interface MyTest {int count() default 1; // 表示注解的属性
}
@Target({ElementType.METHOD, ElementType.FIELD}) // 表示注解的作用目标为方法,成员变量
@Retention(RetentionPolicy.RUNTIME) // 表示注解的保留策略: 编译器运行时(一直活着)
@interface MyTest1 {
}@Target({ElementType.METHOD, ElementType.TYPE}) // 表示注解的作用目标为方法,成员变量
@Retention(RetentionPolicy.RUNTIME) // 表示注解的保留策略: 编译器运行时(一直活着)
@interface MyTest2 {String value();double height() default 169.5;String[] address();
}@ADVZc6.MyInterface(name = "赵丽颖", age = 18, address = {"北京", "上海"})
//@MyInterface(age = "delete")
//@MyInterface("delete") // 特殊属性value,在使用时如果只有一个value属性,value名称可以不写
class AnnotationDemo1 {@ADVZc6.MyInterface(name = "王菲", age = 52, address = {"北京", "香港"})public static void main( String[] args ) {// 目标:自定义注解。int a;}
}//@MyTest1
class AnnotationDemo2 {private int age;//@MyTest1public AnnotationDemo2(){}public static void main(String[] args) {//元注解的作用}public void getAgeTest(){}
}class AnnotationDemo4 {// 注解的应用场景:模拟junit框架。有MyTest注解的方法就执行,没有的就不执行public static void main(String[] args) throws Exception {AnnotationDemo4 ad = new AnnotationDemo4();// 1、获取类对象Class c = AnnotationDemo4.class;// 2、获取所有方法Method[] methods = c.getMethods();// 3、遍历所有方法,判断方法上是否有MyTest注解,有就执行,没有就不执行。for (Method method : methods) {// 4、判断方法上是否有MyTest注解if (method.isAnnotationPresent(MyTest.class)) {// 获取到这个方法的注解MyTest myTest = method.getDeclaredAnnotation(MyTest.class);int count = myTest.count();// 5、有就执行这个method方法for (int i = 0; i < count; i++) {method.invoke(ad);}}}}// 测试方法:public 无参 无返回值@MyTestpublic void test1(){System.out.println("test1方法执行了");}public void test2(){System.out.println("test2方法执行了");}@MyTest(count = 2)public void test3(){System.out.println("test3方法执行了");}@MyTestpublic void test4(){System.out.println("test4方法执行了");}
}
package ADV_0;import org.junit.Test;import java.lang.reflect.Method;
import java.util.Arrays;public class AnnotationDemo3 {//解析注解@Testpublic void parseClass() throws Exception {// 1.获取类对象Class c1 = Demo.class;// 2、使用isAnnotationPresent判断这个类上是否陈列了注解MyTest2if (c1.isAnnotationPresent(MyTest2.class)) {// 3、获取注解对象MyTest2 myTest2 = (MyTest2) c1.getDeclaredAnnotation(MyTest2.class);// 4、获取注解属性值String[] address = myTest2.address();double height = myTest2.height();String value = myTest2.value();// 5、打印注解属性值System.out.println(Arrays.toString(address));System.out.println(height);System.out.println(value);}}@Testpublic void parseMethod() throws Exception {// 1.获取类对象Class c1 = Demo.class;// 2、获取方法对象Method method = c1.getMethod("go");// 3、使用isAnnotationPresent判断这个方法上是否陈列了注解MyTest2if (method.isAnnotationPresent(MyTest2.class)) {// 4、获取注解对象MyTest2 myTest2 = method.getDeclaredAnnotation(MyTest2.class);// 5、获取注解属性值String[] address = myTest2.address();double height = myTest2.height();String value = myTest2.value();// 6、打印注解属性值System.out.println(Arrays.toString(address));System.out.println(height);System.out.println(value);}}
}
动态代理
package ADV_0;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** 动态代理示例代码* 包含:接口定义、真实对象、调用处理器、客户端演示*/
public class ADVZc7 {// 1. 定义业务接口interface UserService {void addUser(String username);String getUser(int userId);}// 2. 真实对象(被代理类)static class UserServiceImpl implements UserService {@Overridepublic void addUser(String username) {System.out.println("真实方法: 添加用户 " + username);}@Overridepublic String getUser(int userId) {return "真实用户" + userId;}}// 3. 调用处理器(实现InvocationHandler)static class LoggingHandler implements InvocationHandler {private final Object target;  // 持有真实对象引用public LoggingHandler(Object target) {this.target = target;}/*** 代理方法调用的核心逻辑* @param proxy  代理对象(慎用)* @param method 被调用的方法对象* @param args   方法参数*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 前置增强System.out.println("[日志] 开始执行方法: " + method.getName());// 反射调用真实对象的方法Object result = method.invoke(target, args);// 后置增强System.out.println("[日志] 方法执行完成: " + method.getName());return result;}}public static void main(String[] args) {// 4. 创建真实对象UserService realService = new UserServiceImpl();// 5. 创建调用处理器InvocationHandler handler = new LoggingHandler(realService);// 6. 动态生成代理对象UserService proxyService = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(), // 类加载器new Class[]{UserService.class},     // 代理接口数组handler                             // 调用处理器);// 7. 通过代理对象调用方法proxyService.addUser("张三");System.out.println("查询结果:" + proxyService.getUser(1001));// 打印代理类信息System.out.println("\n代理类名称: " + proxyService.getClass().getName());System.out.println("代理类父类: " + proxyService.getClass().getSuperclass());System.out.println("代理类实现的接口: ");for (Class<?> inter : proxyService.getClass().getInterfaces()) {System.out.println(" - " + inter.getName());}}
}

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

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

相关文章

爬虫基础(四)线程 和 进程 及相关知识点

目录 一、线程和进程 &#xff08;1&#xff09;进程 &#xff08;2&#xff09;线程 &#xff08;3&#xff09;区别 二、串行、并发、并行 &#xff08;1&#xff09;串行 &#xff08;2&#xff09;并行 &#xff08;3&#xff09;并发 三、爬虫中的线程和进程 &am…

自签证书的dockerfile中from命令无法拉取镜像而docker的pull命令能拉取镜像

问题现象&#xff1a; docker pull images拉取镜像正常 dockerfile中的from命令拉取镜像就会报出证书错误。报错信息如下&#xff1a; [bjxtbwj-kvm-test-jenkins-6-243 ceshi_dockerfile]$ docker build . [] Building 0.4s (3/3) FINISHED …

计算机网络 IP 网络层 2 (重置版)

IP的简介&#xff1a; IP 地址是互联网协议地址&#xff08;Internet Protocol Address&#xff09;的简称&#xff0c;是分配给连接到互联网的设备的唯一标识符&#xff0c;用于在网络中定位和通信。 IP编制的历史阶段&#xff1a; 1&#xff0c;分类的IP地址&#xff1a; …

面对企业文件交换难题,镭速跨网文件交换系统是如何解决的?

在当今这个数字化快速发展的时代&#xff0c;企业越来越依赖于数据交换来维持其业务运作。无论是内部网络之间的沟通还是与外部合作伙伴的数据共享&#xff0c;高效且安全的跨网文件交换都显得尤为重要。然而&#xff0c;在实际操作中&#xff0c;许多企业面临着各种各样的挑战…

Many Whelps! Handle It! (10 player) Many Whelps! Handle It! (25 player)

http://db.nfuwow.com/80/?achievement4403 http://db.nfuwow.com/80/?achievement4406 最少扣你50DKP! 第二阶段 当奥妮克希亚升空后&#xff0c;在10秒内引出50只奥妮克希亚雏龙&#xff0c;随后击败奥妮克希亚。 World of Warcraft [CLASSIC][80猎人][Grandel][最少扣你5…

自制虚拟机(C/C++)(一、分析语法和easyx运用,完整虚拟机实现)

网上对虚拟机的解释很多&#xff0c;其实本质就一句话 虚拟机就是机器语言解释器 我们今天要实现汇编语言解释器&#xff0c;下一次再加上ndisasm反汇编器就是真正虚拟机了 注:这里的虚拟机指的是VMware一类的&#xff0c;而不是JVM&#xff0c;python一样的高级语言解释器 …

36. printf

1. printf 格式化函数说的是 printf、 sprintf 和 scanf 这样的函数&#xff0c;分为格式化输入和格式化输出两类函数。学习 C 语言的时候常常通过 printf 函数在屏幕上显示字符串&#xff0c;通过 scanf 函数从键盘获取输入。这样就有了输入和输出了&#xff0c;实现了最基本…

实验八 JSP访问数据库

实验八 JSP访问数据库 目的&#xff1a; 1、熟悉JDBC的数据库访问模式。 2、掌握使用My SQL数据库的使用 实验要求&#xff1a; 1、通过JDBC访问mysql数据&#xff0c;实现增删改查功能的实现 2、要求提交实验报告&#xff0c;将代码和实验结果页面截图放入报告中 实验过程&a…

python学opencv|读取图像(四十六)使用cv2.bitwise_or()函数实现图像按位或运算

【0】基础定义 按位与运算&#xff1a;全1取1&#xff0c;其余取0。按位或运算&#xff1a;全0取0&#xff0c;其余取1。 【1】引言 前序学习进程中&#xff0c;已经对图像按位与计算进行了详细探究&#xff0c;相关文章链接如下&#xff1a; python学opencv|读取图像&…

使用vhd虚拟磁盘安装两个win10系统

使用vhd虚拟磁盘安装两个win10系统 前言vhd虚拟磁盘技术简介准备工具开始动手实践1.winX选择磁盘管理2.选择“操作”--“创建VHD”3.自定义一个位置&#xff0c;输入虚拟磁盘大小4.右键初始化磁盘5.选择GPT分区表格式6.右键新建简单卷7.给卷起个名字&#xff0c;用于区分8.打开…

基于云计算、大数据与YOLO设计的火灾/火焰目标检测

摘要&#xff1a;本研究针对火灾早期预警检测需求&#xff0c;采用在Kaggle平台获取数据、采用云计算部署的方式&#xff0c;以YOLOv11构建模型&#xff0c;使用云计算服务器训练模型。经训练&#xff0c;box loss从约3.5降至1.0&#xff0c;cls loss从约4.0降至1.0&#xff0c…

计算机毕业设计Python+CNN卷积神经网络考研院校推荐系统 考研分数线预测 考研推荐系统 考研爬虫 考研大数据 Hadoop 大数据毕设 机器学习

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

小程序-基础加强-自定义组件

前言 这次讲自定义组件 1. 准备今天要用到的项目 2. 初步创建并使用自定义组件 这样就成功在home中引入了test组件 在json中引用了这个组件才能用这个组件 现在我们来实现全局引用组件 在app.json这样使用就可以了 3. 自定义组件的样式 发现页面里面的文本和组件里面的文…

docker安装emqx

emqx安装 拉取emqx镜像 docker pull emqx/emqx:v4.1.0 运行docker容器 docker run -tid --name emqx -p 1883:1883 -p 8083:8083 -p 8081:8081 -p 8883:8883 -p 8084:8084 -p 18083:18083 emqx/emqx:v4.1.0 放行端口 1、如果要是自己的虚拟机&#xff0c;并且关闭了防火墙&a…

【4Day创客实践入门教程】Day4 迈向高手之路——进一步学习!

Day4 迈向高手之路——进一步学习&#xff01; 目录 Day4 迈向高手之路——进一步学习&#xff01;更多的开发板外壳制作 Day0 创想启程——课程与项目预览Day1 工具箱构建——开发环境的构建Day2 探秘微控制器——单片机与MicroPython初步Day3 实战演练——桌面迷你番茄钟Day4…

深度学习之“缺失数据处理”

缺失值检测 缺失数据就是我们没有的数据。如果数据集是由向量表示的特征组成&#xff0c;那么缺失值可能表现为某些样本的一个或多个特征因为某些原因而没有测量的值。通常情况下&#xff0c;缺失值由特殊的编码方式。如果正常值都是正数&#xff0c;那么缺失值可能被标记为-1…

日志收集Day007

1.配置ES集群TLS认证: (1)elk101节点生成证书文件 cd /usr/share/elasticsearch ./bin/elasticsearch-certutil cert -out config/elastic-certificates.p12 -pass "" --days 3650 (2)elk101节点为证书文件修改属主和属组 chown elasticsearch:elasticsearch con…

arm-linux-gnueabihf安装

Linaro Releases windows下打开wsl2中的ubuntu&#xff0c;资源管理器中输入&#xff1a; \\wsl$gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz 复制到/home/ark01/tool 在 Ubuntu 中创建目录&#xff1a; /usr/local/arm&#xff0c;命令如下&#xff1a; …

LabVIEW透镜多参数自动检测系统

在现代制造业中&#xff0c;提升产品质量检测的自动化水平是提高生产效率和准确性的关键。本文介绍了一个基于LabVIEW的透镜多参数自动检测系统&#xff0c;该系统能够在单一工位上完成透镜的多项质量参数检测&#xff0c;并实现透镜的自动搬运与分选&#xff0c;极大地提升了检…

【算法】动态规划专题① ——线性DP python

目录 引入简单实现稍加变形举一反三实战演练总结 引入 楼梯有个台阶&#xff0c;每次可以一步上1阶或2阶。一共有多少种不同的上楼方法&#xff1f; 怎么去思考&#xff1f; 假设就只有1个台阶&#xff0c;走法只有&#xff1a;1 只有2台阶&#xff1a; 11&#xff0c;2 只有3台…