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 优势
- 独立测试单个方法,不影响其他代码
- 一键运行,结果清晰
- 支持批量测试、断言校验
- 企业开发标准测试工具
二、反射:
反射是 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 反射的作用
- 框架底层核心:Spring 创建对象、MyBatis 封装结果全靠反射
- 突破权限限制:操作私有成员变量 / 方法
- 解耦:不硬编码,动态加载类、动态操作对象
- 通用性强:一套代码操作任意对象
三、注解:
注解 是 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 注解的应用场景
- 框架配置 :Spring 的**@Controller**、@Service
- 数据校验 :@NotNull、@Length
- 单元测试 :JUnit 的**@Test**
- 自定义功能:日志、权限控制
四、动态代理:
动态代理 是企业开发核心技术 ,用来不修改原代码的前提下,增强方法的功能(比如加日志、计时、事务)。
4.1 代理概述
通俗理解
代理 就是中介:
- **目标对象:**真实做事的对象(如房东)
- **代理对象:**代替目标对象做事(如中介)
- **作用:**中介可以在房东租房前后,增加额外服务(带看、签合同)
分类
- **静态代理:**手动写代理类(麻烦,不通用)
- **动态代理:**自动生成代理类(企业首选)
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 动态代理的优势
- 无侵入性:不改动原始业务代码
- 通用性强:一个代理工具类,可代理任意接口的实现类
- 功能强大:统一增强所有方法,高效便捷
- 框架核心:Spring AOP(面向切面编程)底层就是动态代理
五、全文总结
| 技术 | 核心作用 | 应用场景 |
|---|---|---|
| JUnit | 单元测试,快速验证方法 | 方法测试、项目校验 |
| 反射 | 运行时动态操作类的成分 | 框架底层、对象创建 |
| 注解 | 给代码打标记,替代配置文件 | Spring、MyBatis 配置 |
| 动态代理 | 不修改源码增强方法功能 | Spring AOP、日志、事务 |