十七、Java 高级技术入门全解:JUnit、反射、注解、动态代理

Java 基础语法是入门,Java 高级技术才是进阶开发、看懂框架底层的核心!今天我们用最通俗的语言、最简单的代码,带大家吃透 4 个必学高级知识点:JUnit 单元测试、反射、注解、动态代理。


目录

前言

[一、JUnit 单元测试:](#一、JUnit 单元测试:)

[1.1 什么是 JUnit?](#1.1 什么是 JUnit?)

[1.2 快速入门](#1.2 快速入门)

[1. 引入依赖(Maven 项目)](#1. 引入依赖(Maven 项目))

[2. 核心注解](#2. 核心注解)

[3. 代码示例](#3. 代码示例)

[4. 运行方式](#4. 运行方式)

[1.3 JUnit 优势](#1.3 JUnit 优势)

二、反射:

[2.1 反射概述](#2.1 反射概述)

通俗理解

核心对象

[2.2 获取 Class 对象的 3 种方式](#2.2 获取 Class 对象的 3 种方式)

[2.3 反射获取类的成分并操作](#2.3 反射获取类的成分并操作)

[1. 获取构造器 + 创建对象](#1. 获取构造器 + 创建对象)

[2. 获取成员变量 + 赋值 / 取值](#2. 获取成员变量 + 赋值 / 取值)

[3. 获取成员方法 + 调用方法](#3. 获取成员方法 + 调用方法)

[2.4 反射的作用](#2.4 反射的作用)

三、注解:

[3.1 注解概述](#3.1 注解概述)

[3.2 自定义注解](#3.2 自定义注解)

语法

使用注解

[3.3 元注解(注解的注解)](#3.3 元注解(注解的注解))

示例

[3.4 注解的解析](#3.4 注解的解析)

[3.5 注解的应用场景](#3.5 注解的应用场景)

四、动态代理:

[4.1 代理概述](#4.1 代理概述)

通俗理解

分类

[4.2 JDK 动态代理代码实现](#4.2 JDK 动态代理代码实现)

前提

[1. 定义接口(目标接口)](#1. 定义接口(目标接口))

[2. 目标实现类](#2. 目标实现类)

[3. 动态代理工具类](#3. 动态代理工具类)

[4. 测试动态代理](#4. 测试动态代理)

运行结果

[4.3 动态代理解决的实际问题](#4.3 动态代理解决的实际问题)

[4.4 动态代理的优势](#4.4 动态代理的优势)

五、全文总结


前言

本文覆盖四大技术层层递进:JUnit 负责测试、反射负责动态操作对象、注解负责标记配置、动态代理负责方法增强,是 Spring、MyBatis 等主流框架的底层核心!


一、JUnit 单元测试:

在学习高级技术前,我们先掌握JUnit 单元测试 ------ 它是 Java 官方推荐的测试工具,专门用来快速测试单个方法,替代繁琐的main方法。


1.1 什么是 JUnit?

JUnit 是 Java 的单元测试框架 ,可以对项目中的单个方法进行独立测试,不用写主方法,一键运行、一键查看结果。

场景:写了一个计算方法,想单独测试是否正确,用 JUnit 最方便!


1.2 快速入门

1. 引入依赖(Maven 项目)

XML 复制代码
<!-- JUnit5核心依赖 -->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.9.2</version>
    <scope>test</scope>
</dependency>

2. 核心注解

注解 作用
@Test 标记方法为测试方法(核心)
@BeforeEach 每个测试方法执行前运行
@AfterEach 每个测试方法执行后运行
@BeforeAll 所有方法执行前运行一次
@AfterAll 所有方法执行后运行一次

3. 代码示例

java 复制代码
import org.junit.jupiter.api.Test;

public class JUnitDemo {
    // 测试方法:无返回值、无参数
    @Test
    public void testAdd(){
        int sum = 1 + 1;
        System.out.println("求和结果:" + sum); // 运行后直接输出
    }

    @Test
    public void testSub(){
        int sub = 5 - 3;
        System.out.println("求差结果:" + sub);
    }
}

4. 运行方式

直接点击方法左侧的运行按钮,即可单独测试该方法,控制台输出结果。


1.3 JUnit 优势

  1. 独立测试单个方法,不影响其他代码
  2. 一键运行,结果清晰
  3. 支持批量测试、断言校验
  4. 企业开发标准测试工具

二、反射:

反射是 Java最核心的高级特性,框架的底层全靠反射实现!


2.1 反射概述

通俗理解

反射 就是:在程序运行时,拿到任意一个类的所有成分(构造器、变量、方法),并对其进行操作 。就像给类照镜子,能看到它的全部细节,哪怕是private私有成员,也能操作!

核心对象

Class对象:反射的入口,一个类对应唯一的Class对象。


2.2 获取 Class 对象的 3 种方式

java 复制代码
public class ReflectTest {
    public static void main(String[] args) throws ClassNotFoundException {
        // 方式1:类名.class (最常用)
        Class<?> c1 = Student.class;

        // 方式2:对象.getClass()
        Student s = new Student();
        Class<?> c2 = s.getClass();

        // 方式3:Class.forName("全类名") (框架底层用)
        Class<?> c3 = Class.forName("com.demo.Student");
    }
}

// 测试用的学生类
class Student{
    private String name;
    private int age;

    public Student() {}
    private Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

2.3 反射获取类的成分并操作

反射 可以获取构造器、成员变量、成员方法,并突破权限限制操作私有成员。

1. 获取构造器 + 创建对象

java 复制代码
import java.lang.reflect.Constructor;

public class ReflectConstructor {
    public static void main(String[] args) throws Exception {
        Class<?> c = Student.class;

        // 1. 获取public构造器
        Constructor<?> con1 = c.getConstructor();
        Object obj1 = con1.newInstance(); // 创建对象
        System.out.println(obj1);

        // 2. 获取private构造器(暴力反射)
        Constructor<?> con2 = c.getDeclaredConstructor(String.class, int.class);
        con2.setAccessible(true); // 关闭权限检查(核心!)
        Object obj2 = con2.newInstance("小明", 18);
        System.out.println(obj2);
    }
}

2. 获取成员变量 + 赋值 / 取值

java 复制代码
import java.lang.reflect.Field;

public class ReflectField {
    public static void main(String[] args) throws Exception {
        Class<?> c = Student.class;
        Object obj = c.newInstance();

        // 获取私有变量name
        Field nameField = c.getDeclaredField("name");
        nameField.setAccessible(true); // 破解私有

        // 赋值
        nameField.set(obj, "小红");
        // 取值
        System.out.println(nameField.get(obj)); // 输出:小红
    }
}

3. 获取成员方法 + 调用方法

java 复制代码
import java.lang.reflect.Method;

public class ReflectMethod {
    public static void main(String[] args) throws Exception {
        Class<?> c = Student.class;
        Object obj = c.newInstance();

        // 获取方法
        Method method = c.getDeclaredMethod("setName", String.class);
        // 调用方法:对象、参数
        method.invoke(obj, "小刚");
    }
}

2.4 反射的作用

  1. 框架底层核心:Spring 创建对象、MyBatis 封装结果全靠反射
  2. 突破权限限制:操作私有成员变量 / 方法
  3. 解耦:不硬编码,动态加载类、动态操作对象
  4. 通用性强:一套代码操作任意对象

三、注解:

注解 是 Java 的配置标记 ,不是注释,是给JVM / 框架看的标记,用来替代繁琐的 XML 配置。


3.1 注解概述

  • **注解本质:**一个特殊的接口,用于给代码添加标记
  • 常见内置注解:@Override (重写)、@Test(测试)、@FunctionalInterface(函数式接口)

3.2 自定义注解

语法

java 复制代码
// 自定义注解:@interface + 属性
public @interface MyTest {
    // 注解属性:类型 + 属性名() [default 默认值]
    String value() default "";
    int age() default 18;
}

使用注解

java 复制代码
// 给类标记注解
@MyTest(value = "测试类", age = 20)
public class User {
}

3.3 元注解(注解的注解)

元注解用来修饰自定义注解的注解。

Java 提供 4 个核心元注解,最常用 2 个:

元注解 作用
@Target 指定注解能用在什么位置(类、方法、变量)
@Retention 指定注解的生命周期(源码 / 编译 / 运行时)

示例

java 复制代码
import java.lang.annotation.*;

// 注解只能用在类、方法上
@Target({ElementType.TYPE, ElementType.METHOD})
// 注解保留到运行时(反射能读取)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String name();
}

3.4 注解的解析

注解必须配合反射才能生效!通过反射读取类 / 方法上的注解,实现业务逻辑。

java 复制代码
import java.lang.reflect.Method;

public class AnnotationParse {
    public static void main(String[] args) throws Exception {
        Class<?> c = User.class;
        // 1. 判断类上是否有注解
        if(c.isAnnotationPresent(MyAnnotation.class)){
            // 2. 获取注解对象
            MyAnnotation anno = c.getAnnotation(MyAnnotation.class);
            // 3. 获取注解属性值
            System.out.println(anno.name());
        }
    }
}

3.5 注解的应用场景

  1. 框架配置 :Spring 的**@Controller**、@Service
  2. 数据校验@NotNull@Length
  3. 单元测试 :JUnit 的**@Test**
  4. 自定义功能:日志、权限控制

四、动态代理:

动态代理 是企业开发核心技术 ,用来不修改原代码的前提下,增强方法的功能(比如加日志、计时、事务)。


4.1 代理概述

通俗理解

代理 就是中介

  1. **目标对象:**真实做事的对象(如房东)
  2. **代理对象:**代替目标对象做事(如中介)
  3. **作用:**中介可以在房东租房前后,增加额外服务(带看、签合同)

分类

  1. **静态代理:**手动写代理类(麻烦,不通用)
  2. **动态代理:**自动生成代理类(企业首选)

4.2 JDK 动态代理代码实现

前提

JDK 动态代理必须基于接口实现。

1. 定义接口(目标接口)

java 复制代码
package Proxy;
// 明星行为接口
public interface StarService {
    void sing(String name);
    String dance();
}

2. 目标实现类

java 复制代码
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Star implements StarService{
    private String name;

    @Override
    public void sing(String name) {
        System.out.println(this.name + "表演唱歌:" + name);
    }

    @Override
    public String dance() {
        System.out.println(this.name + "表演跳舞:魅力四射!" );
        return "谢谢!谢谢!";
    }
}

3. 动态代理工具类

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 代理工具类:中介公司,专门负责创建代理对象并返回给别人使用
 */
public class ProxyUtil {
    // 创建一个明星对象的代理对象返回。
    public static StarService createProxy(Star s){
        /**
         * 参数一:用于执行用哪个类加载器去加载生成的代理类。
         * 参数二:用于指定代理类需要实现的接口: 明星类实现了哪些接口,代理类就实现哪些接口
         * 参数三:用于指定代理类需要如何去代理(代理要做的事情)。
         */
        StarService proxy = (StarService) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
                s.getClass().getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 用来声明代理对象要干的事情。
                        // 参数一: proxy接收到代理对象本身(暂时用处不大)
                        // 参数二: method代表正在被代理的方法
                        // 参数三: args代表正在被代理的方法的参数
                        String methodName = method.getName();
                        if("sing".equals(methodName)){
                            System.out.println("准备话筒,收钱20万!");
                        }else if("dance".equals(methodName)){
                            System.out.println("准备场地,收钱100万!");
                        }
                        // 真正干活(把真正的明星对象叫过来正式干活)
                        // 找真正的明星对象来执行被代理的行为:method方法
                        Object result = method.invoke(s, args);
                        return result;
                    }
                });
        return proxy;
    }
}

4. 测试动态代理

java 复制代码
import java.lang.reflect.Proxy;

public class Test {
    public static void main(String[] args) {
        // 目标:创建代理对象。
        // 1、准备一个明星对象:设计明星类。
        Star star = new Star("章");
        // 2、为章创建一个专属与她的代理对象。
        StarService proxy = ProxyUtil.createProxy(star);
        proxy.sing("《红》");
        System.out.println(proxy.dance());
    }
}

运行结果

java 复制代码
准备话筒,收钱20万!
章表演唱歌:《红》
准备场地,收钱100万!
章表演跳舞:魅力四射!
谢谢!谢谢!

4.3 动态代理解决的实际问题

不修改原代码,增强方法功能统一添加:日志、耗时统计、事务管理、权限校验解决代码冗余问题,提高复用性。


4.4 动态代理的优势

  1. 无侵入性:不改动原始业务代码
  2. 通用性强:一个代理工具类,可代理任意接口的实现类
  3. 功能强大:统一增强所有方法,高效便捷
  4. 框架核心:Spring AOP(面向切面编程)底层就是动态代理

五、全文总结

技术 核心作用 应用场景
JUnit 单元测试,快速验证方法 方法测试、项目校验
反射 运行时动态操作类的成分 框架底层、对象创建
注解 给代码打标记,替代配置文件 Spring、MyBatis 配置
动态代理 不修改源码增强方法功能 Spring AOP、日志、事务
相关推荐
染翰11 分钟前
Nacos 切换 Namespace 后配置不生效、占位符报错终极复盘
java·后端·spring·nacos
terry60011 分钟前
2026图形验证码服务商横向测评|口碑、接入、安全选型全指南
java·大数据·人工智能·web安全·信息与通信·数据库架构
阿坤带你走近大数据22 分钟前
java中泛型不能用基础数据类型
java·开发语言
skywalker_1124 分钟前
SpringBoot速通(实战教学)
java·spring boot·redis·rpc·ssm·mybatis-plus
weixin_3077791325 分钟前
从脚本执行到智能体协作:AI辅助测试能力的范式重构
运维·开发语言·人工智能·算法·测试用例
量化君也26 分钟前
从回测到全自动实盘交易,全天候策略需要经历哪些改造?
大数据·人工智能·python·算法·金融
云絮.36 分钟前
增删改查操作
java·开发语言
themingyi43 分钟前
Abaqus2024安装python包pandas
开发语言·python·pandas
阿坤带你走近大数据1 小时前
Linux中管道符的作用
java·linux·服务器
阿正的梦工坊1 小时前
【Rust】19-FFI、ABI 与跨语言边界设计
开发语言·后端·rust