十七、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、日志、事务
相关推荐
卓怡学长2 小时前
w1基于springboot高校学生评教系统
java·spring boot·tomcat·maven·intellij-idea
ruan1145142 小时前
关于HashMap--个人学习记录
java·jvm·servlet
yongui478342 小时前
MATLAB模糊控制的粒子群算法(Fuzzy-PSO)实现
数据结构·算法·matlab
sali-tec2 小时前
C# 基于OpenCv的视觉工作流-章49-人脸检测
图像处理·人工智能·opencv·算法·计算机视觉
不爱吃炸鸡柳2 小时前
4道经典算法题代码详解:从两数之和到链表两两交换
算法·链表·哈希算法
cmpxr_2 小时前
【C】隐式类型转换
c语言·c++·算法
lvyuanj2 小时前
Java AI开发实战:Spring AI完全指南
java·人工智能·spring
W23035765732 小时前
【C++ 高性能日志系统实战】第三篇:异步日志系统的实现与优化
网络·数据结构·算法·日志
Dxy12393102162 小时前
Python使用SymSpell详解:打造极速拼写检查引擎
开发语言·python