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

news2025/2/1 13:34:31

前言

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核心)
    @Test
    public 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 = 25
        System.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;
    //@MyTest1
    public 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 无参 无返回值
    @MyTest
    public void test1(){
        System.out.println("test1方法执行了");
    }
    public void test2(){
        System.out.println("test2方法执行了");
    }
    @MyTest(count = 2)
    public void test3(){
        System.out.println("test3方法执行了");
    }
    @MyTest
    public 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 {
    //解析注解
    @Test
    public void parseClass() throws Exception {
        // 1.获取类对象
        Class c1 = Demo.class;
        // 2、使用isAnnotationPresent判断这个类上是否陈列了注解MyTest2
        if (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);
        }
    }

    @Test
    public void parseMethod() throws Exception {
        // 1.获取类对象
        Class c1 = Demo.class;
        // 2、获取方法对象
        Method method = c1.getMethod("go");
        // 3、使用isAnnotationPresent判断这个方法上是否陈列了注解MyTest2
        if (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 {
        @Override
        public void addUser(String username) {
            System.out.println("真实方法: 添加用户 " + username);
        }
        @Override
        public 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   方法参数
         */
        @Override
        public 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.coloradmin.cn/o/2289318.html

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

相关文章

STM32 LED呼吸灯

接线图&#xff1a; 这里将正极接到PA0引脚上&#xff0c;负极接到GND&#xff0c;这样就高电平点亮LED&#xff0c;低电平熄灭。 占空比越大&#xff0c;LED越亮&#xff0c;占空比越小&#xff0c;LED越暗 PWM初始化配置 输出比较函数介绍&#xff1a; 用这四个函数配置输…

栈和队列特别篇:栈和队列的经典算法问题

图均为手绘,代码基于vs2022实现 系列文章目录 数据结构初探: 顺序表 数据结构初探:链表之单链表篇 数据结构初探:链表之双向链表篇 链表特别篇:链表经典算法问题 数据结构:栈篇 数据结构:队列篇 文章目录 系列文章目录前言一.有效的括号(leetcode 20)二.用队列实现栈(leetcode…

2024年数据记录

笔者注册时间超过98.06%的用户 CSDN 原力是衡量一个用户在 CSDN 的贡献和影响力的系统&#xff0c;笔者原力值超过99.99%的用户 其他年度数据

DBO优化最近邻分类预测matlab

蜣螂优化算法&#xff08;Dung Beetle Optimizer&#xff0c;简称 DBO&#xff09;作为一种新兴的群智能优化算法&#xff0c;于 2022 年末被提出&#xff0c;其灵感主要来源于蜣螂的滚球、跳舞、觅食、偷窃以及繁殖等行为。 本次使用的数据为 Excel 格式的分类数据集。该数据…

PSpice for TI体验

前言 基于 从零开始学 PSpice for TI 仿真工具 - 手把手操作实训课程_哔哩哔哩_bilibili 体验PSpice for TI的功能&#xff0c;并记录下来。文章内容大部分都参考自视频&#xff0c;可以理解成图文版。目前发现是没有支持中文语言&#xff0c;而且部分仿真&#xff0c;时间消耗…

苯乙醇苷类化合物的从头生物合成-文献精读108

Complete pathway elucidation of echinacoside in Cistanche tubulosa and de novo biosynthesis of phenylethanoid glycosides 管花肉苁蓉中松果菊苷全生物合成途径解析及苯乙醇苷类化合物的从头生物合成 摘要 松果菊苷&#xff08;ECH&#xff09;是最具代表性的苯乙醇苷…

【C++】设计模式详解:单例模式

文章目录 Ⅰ. 设计一个类&#xff0c;不允许被拷贝Ⅱ. 请设计一个类&#xff0c;只能在堆上创建对象Ⅲ. 请设计一个类&#xff0c;只能在栈上创建对象Ⅳ. 请设计一个类&#xff0c;不能被继承Ⅴ. 请设计一个类&#xff0c;只能创建一个对象&#xff08;单例模式&#xff09;&am…

解决vsocde ssh远程连接同一ip,不同端口情况下,无法区分的问题

一般服务器会通过镜像分身或者容器的方式&#xff0c;一个ip分出多个端口给多人使用&#xff0c;但如果碰到需要连接同一user&#xff0c;同一个ip,不同端口的情况&#xff0c;vscode就无法识别&#xff0c;如下图所示&#xff0c;vscode无法区分该ip下不同端口的连接&#xff…

AJAX案例——图片上传个人信息操作

黑马程序员视频地址&#xff1a; AJAX-Day02-11.图片上传https://www.bilibili.com/video/BV1MN411y7pw?vd_source0a2d366696f87e241adc64419bf12cab&spm_id_from333.788.videopod.episodes&p26 图片上传 <!-- 文件选择元素 --><input type"file"…

LabVIEW温度修正部件测试系统

LabVIEW温度修正部件测试系统 这个基于LabVIEW的温度修正部件测试系统旨在解决飞行器温度测量及修正电路的测试需求。该系统的意义在于提供一个可靠的测试平台&#xff0c;用于评估温度修正部件在实际飞行器环境中的性能表现&#xff0c;从而确保飞行器的安全性和可靠性。 系统…

细说机器学习算法之ROC曲线用于模型评估

系列文章目录 第一章&#xff1a;Pyhton机器学习算法之KNN 第二章&#xff1a;Pyhton机器学习算法之K—Means 第三章&#xff1a;Pyhton机器学习算法之随机森林 第四章&#xff1a;Pyhton机器学习算法之线性回归 第五章&#xff1a;Pyhton机器学习算法之有监督学习与无监督…

DeepSeek本地部署(windows)

一、下载并安装Ollama 1.下载Ollama Ollama官网:Ollama 点击"Download",会跳转至下载页面。 点击"Download for Windows"。会跳转Github进行下载,如下载速度过慢,可在浏览器安装GitHub加速插件。 2.安装Ollama 双击下载的安装文件,点击"Inst…

简要介绍C语言/C++的三目运算符

三元运算符是C语言和C中的一种简洁的条件运算符&#xff0c;它的形式为&#xff1a; 条件表达式 ? 表达式1 : 表达式2; 三元运算符的含义 条件表达式&#xff1a;这是一个布尔表达式&#xff0c;通常是一个比较操作&#xff08;如 >、<、 等&#xff09;。 表达式1&am…

SpringCloud系列教程:微服务的未来(十九)请求限流、线程隔离、Fallback、服务熔断

前言 前言 在现代微服务架构中&#xff0c;系统的高可用性和稳定性至关重要。为了解决系统在高并发请求或服务不可用时出现的性能瓶颈或故障&#xff0c;常常需要使用一些技术手段来保证服务的平稳运行。请求限流、线程隔离、Fallback 和服务熔断是微服务中常用的四种策略&…

STM32 对射式红外传感器配置

这次用的是STM32F103的开发板&#xff08;这里面的exti.c文件没有how to use this driver 配置说明&#xff09; 对射式红外传感器 由一个红外发光二极管和NPN光电三极管组成&#xff0c;M3固定安装孔&#xff0c;有输出状态指示灯&#xff0c;输出高电平灯灭&#xff0c;输出…

(动态规划路径基础 最小路径和)leetcode 64

视频教程 1.初始化dp数组&#xff0c;初始化边界 2、从[1行到n-1行][1列到m-1列]依次赋值 #include<vector> #include<algorithm> #include <iostream>using namespace std; int main() {vector<vector<int>> grid { {1,3,1},{1,5,1},{4,2,1}…

嵌入式C语言:什么是共用体?

在嵌入式C语言编程中&#xff0c;共用体&#xff08;Union&#xff09;是一种特殊的数据结构&#xff0c;它允许在相同的内存位置存储不同类型的数据。意味着共用体中的所有成员共享同一块内存区域&#xff0c;因此&#xff0c;在任何给定时间&#xff0c;共用体只能有效地存储…

QT简单实现验证码(字符)

0&#xff09; 运行结果 1&#xff09; 生成随机字符串 Qt主要通过QRandomGenerator类来生成随机数。在此之前的版本中&#xff0c;qrand()函数也常被使用&#xff0c;但从Qt 5.10起&#xff0c;推荐使用更现代化的QRandomGenerator类。 在头文件添加void generateRandomNumb…

【4Day创客实践入门教程】Day2 探秘微控制器——单片机与MicroPython初步

Day2 探秘微控制器——单片机与MicroPython初步 目录 Day2 探秘微控制器——单片机与MicroPython初步MicroPython语言基础开始基础语法注释与输出变量模块与函数 单片机基础后记 Day0 创想启程——课程与项目预览Day1 工具箱构建——开发环境的构建Day2 探秘微控制器——单片机…

[论文阅读] (37)CCS21 DeepAID:基于深度学习的异常检测(解释)

祝大家新春快乐&#xff0c;蛇年吉祥&#xff01; 《娜璋带你读论文》系列主要是督促自己阅读优秀论文及听取学术讲座&#xff0c;并分享给大家&#xff0c;希望您喜欢。由于作者的英文水平和学术能力不高&#xff0c;需要不断提升&#xff0c;所以还请大家批评指正&#xff0…