枚举

枚举是Java中一种特殊类型,一般用来做信息的标记和分类

在不是枚举的情况下,也可以实现信息的标记和分类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package _enum;

public class EnumDemo01 {
public static void main(String[] args) {
useSeason(Season.AUTUMN);
}

public static void useSeason(String string) {
switch (string) {
case Season.SPRING -> System.out.println("春天");
case Season.SUMMER -> System.out.println("夏天");
case Season.AUTUMN -> System.out.println("秋天");
case Season.WINTER -> System.out.println("冬天");
}
}
}

class Season {
public static final String SPRING = "SPRING";
public static final String SUMMER = "SUMMER";
public static final String AUTUMN = "AUTUMN";
public static final String WINTER = "WINTER";

// 私有构造函数,防止实例化
private Season() {}

public static String[] getAllSeasons() {
return new String[]{SPRING, SUMMER, AUTUMN, WINTER};
}
}

使用枚举技术优化代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package _enum;

public class EnumDemo01 {
public static void main(String[] args) {
useSeason(Season.AUTUMN);
}

public static void useSeason(Season season) {
switch (season) {
case SPRING -> System.out.println("春天");
case SUMMER -> System.out.println("夏天");
case AUTUMN -> System.out.println("秋天");
case WINTER -> System.out.println("冬天");
}
}

}

enum Season {
SPRING, SUMMER, AUTUMN, WINTER;
}

枚举入参严谨,提示性更好,代码优雅

枚举定义格式:

1
2
3
修饰符 enum 枚举类名 {
枚举项1, 枚举项2, 枚举项3...
}

枚举的特点

  1. 每一个枚举项其实就是该枚举类的一个对象
  2. 通过枚举类名去访问指定的枚举项
  3. 所有枚举类都是Enum的子类
  4. 枚举也是类,可以定义成员变量
  5. 枚举类的第一行上必须是枚举项,最后一个枚举项的分号是可以省略的。但如果枚举类有其他东西,就不能省略(推荐不省略)
  6. 枚举类可以有构造器,但必须是private的,它默认也是private
  7. 枚举类也可以有抽象方法,但枚举项必须重写该方法

类加载器

主要作用就是将类的字节码载入方法区中

加载时机:用到就加载。比如创建对象、调用类的静态成员、初始化继承体系、使用反射加载类的字节码

加载过程:加载、链接、初始化

第一步加载:

  • 通过包名 + 类名,获取这个类,准备用流进行传输
  • 将类加载到内存中
  • 加载完毕创建一个class对象

第二步链接:

  • 验证:验证类是否符合JVM规范,安全性检查

  • 准备:为static变量分配空间,设置默认值

  • 解析:将常量池中的符号引用解析为直接引用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    package classloader;

    public class ClassLoaderDemo01 {
    public static void main(String[] args) {
    ClassLoader classLoader = B.class.getClassLoader();
    // 代码执行到这,表示:
    // 1. B类的字节码被加载到方法区,并完成链接(验证、准备、解析)
    // 2. C类的字节码也会被加载到方法区
    // 3. 此时B和C都只是类结构,没有创建任何实例对象
    // 4. B类中对C的引用是符号引用,尚未解析为直接引用

    B b = new B();
    // 当创建B对象时:
    // 1. 在堆中为B实例分配内存
    // 2. 执行B的实例初始化
    // 3. 在初始化过程中,执行 C c = new C(),创建C的实例
    // 4. 此时C的实例才在堆中分配内存,有了实际的地址值
    }
    }

    class B {
    C c = new C();
    }

    class C {

    }

第三步初始化:

  • 初始化:根据程序员程序编码制定的主观计划去初始化类变量和其他资源
  • 可以理解为将类中的静态成员变量赋上真实的值

类加载器的分类

  • Bootstrap class loader:启动类加载器,虚拟机内置类加载器,通常表示为null。因为这个加载器是利用C++实现,所以获取到的只能是null
  • Platfrom class loader:平台类加载器,负责加载JDK中一些特殊的模块。负责加载lib/modules内部的类
    • 平台类加载器是从JDK9版本之后出现的
    • JDK9版本之前是Extension Class Loader,扩展类加载器。负责加载jre/lib/ext目录下的类
  • Application class loader:应用程序类加载器。负责加载自己写的类
  • 自定义类加载器:上级为Application,目前不做讲解
方法 功能
public Classloader getClassLoader() 获取该类的 类加载器对象

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package classloader;

public class ClassLoaderDemo {
public static void main(String[] args) {
// Boostrap类加载器
ClassLoader classLoader1 = String.class.getClassLoader();
System.out.println(classLoader1); // null

// Application类加载器
ClassLoader classLoader2 = ClassLoaderDemo.class.getClassLoader();
System.out.println(classLoader2); // jdk.internal.loader.ClassLoaders$AppClassLoader@36baf30c

// 获取上下级关系
System.out.println(classLoader2.getParent()); // jdk.internal.loader.ClassLoaders$PlatformClassLoader@b4c966a
System.out.println(classLoader2.getParent().getParent()); // jdk.internal.loader.ClassLoaders$PlatformClassLoader@b4c966a

}
}

Application类加载器的上级:Platform类加载器

Platform类加载器的上级:Bootstrap类加载器

双亲委派模式

  1. 如果一个类加载器收到了类加载请求,它并不会自己先去加载
  2. 而是把这个请求委托给父类的加载器去执行
  3. 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归
  4. 请求最终将到达顶层的启动类加载器
  5. 如果父类加载器可以完成类加载任务,就成功返回
  6. 但父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式
  7. 这种加载模式的好处在于避免类的重复加载

Snipaste_2025-08-01_20-07-00

反射

  • 框架技术的灵魂
  • 运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法。对于任意一个对象,都能够调用它的任意属性和方法
  • 这种动态获取信息以及动态调用对象方法的功能成为Java语言的反射机制

获取类的字节码对象

  1. Class.forName(“全类名”);
  2. 类名.class
  3. 对象.getClass();

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package reflect;

import pojo.Student;

public class ReflectDemo01 {
public static void main(String[] args) throws ClassNotFoundException {
// 1. Class.forName("全类名");
Class<?> class1 = Class.forName("pojo.Student");

// 2. 类名.class
Class<Student> class2 = Student.class;

// 3. 对象.getClass();
Student s = new Student();
Class<? extends Student> class3 = s.getClass();

System.out.println(class1); // class pojo.Student
System.out.println(class2); // class pojo.Student
System.out.println(class3); // class pojo.Student
System.out.println((class1 == class2) && (class2 == class3)); // 结果为true,因为字节码文件唯一
}
}

反射类中的构造方法

方法 功能
Constructor<?>[] getConstructors() 返回所有公共构造方法对象的数组
Constructor<?>[] getDeclaredConstructors() 返回所有构造方法对象的数组
Constructor<T> getConstructor(Class<?>… parameterTypes) 返回单个公共构造方法对象
Constructor<T> getDeclaredConstructor(Class<?>… parameterTypes) 返回单个构造方法对象

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package reflect;

import java.lang.reflect.Constructor;
import java.util.Arrays;

public class ReflectDemo02 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
// 获取类的字节码对象
Class<?> class1 = Class.forName("pojo.Student");

// 反射构造方法对象,批量反射
Constructor<?>[] constructors1 = class1.getConstructors();
Constructor<?>[] constructors2 = class1.getDeclaredConstructors();

System.out.println(Arrays.toString(constructors1));
// [public pojo.Student(), public pojo.Student(java.lang.String,int)]
System.out.println(Arrays.toString(constructors2));
// [public pojo.Student(), private pojo.Student(int), private pojo.Student(java.lang.String), public pojo.Student(java.lang.String,int)]

// 反射构造方法对象,单独反射
Constructor<?> constructor1 = class1.getConstructor(String.class, int.class); // 对应构造方法的参数,提供参数的字节码对象
System.out.println(constructor1); // public pojo.Student(java.lang.String,int)

Constructor<?> constructor2 = class1.getDeclaredConstructor(String.class);
System.out.println(constructor2); // private pojo.Student(java.lang.String)
}
}

通过构造方法对象进行实例化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package reflect;

import pojo.Student;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class ReflectDemo03 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<Student> class1 = Student.class;
Constructor<Student> constructor1 = class1.getConstructor(String.class, int.class); // 这是公共的构造方法

// ---------------------------------------------------------------------------- //
Student student1 = constructor1.newInstance("张三", 18);
System.out.println(student1); // Student{name='张三', age=18}

// ---------------------------------------------------------------------------- //
Constructor<Student> constructor2 = class1.getDeclaredConstructor(String.class); // 这是私有的构造方法

// Student student2 = constructor2.newInstance("张三"); // 报错:IllegalAccessException
// System.out.println(student2);

constructor2.setAccessible(true); // 取消权限检查
Student student2 = constructor2.newInstance("张三");
System.out.println(student2); // Student{name='张三', age=0}

}
}

newInstance:实例化对象的方法

setAccessible:设置为true表示取消权限检查

反射类中的成员变量

方法 功能
Field[] getFields() 返回所有公共成员变量对象的数据
Field[] getDeclaredFields() 返回所有成员变量对象的数组
Field getField(String name) 返回单个公共成员变量对象
Field getDeclaredField(String name) 返回单个成员变量对象

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package reflect;

import pojo.Student;

import java.lang.reflect.Field;
import java.util.Arrays;

public class ReflectDemo04 {
public static void main(String[] args) throws NoSuchFieldException {
Class<Student> class1 = Student.class;

Field[] fields1 = class1.getFields();
System.out.println(Arrays.toString(fields1)); // [public int pojo.Student.id]

Field[] fields2 = class1.getDeclaredFields();
System.out.println(Arrays.toString(fields2)); // [public int pojo.Student.id, private java.lang.String pojo.Student.name, private int pojo.Student.age]

Field name = class1.getField("id");
Field age = class1.getDeclaredField("age");
System.out.println(name); // public int pojo.Student.id
System.out.println(age); // private int pojo.Student.age
}
}

Field类的设置和获取方法:

方法 说明
void set(Object obj,Object value) 赋值
Object get(Object obj) 获取值

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package reflect;

import pojo.Student;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public class ReflectDemo05 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
Class<Student> studentClass = Student.class;

// 利用反射创建对象
Student student1 = studentClass.getConstructor().newInstance();
Student student2 = studentClass.getConstructor().newInstance();

// 获取成员变量对象
Field name = studentClass.getDeclaredField("name");
Field age = studentClass.getDeclaredField("age");

// 取消权限检查
name.setAccessible(true);
age.setAccessible(true);

// 赋值操作
name.set(student1,"张三");
age.set(student1,18);

name.set(student2,"李四");
age.set(student2,20);

// 打印对象信息
System.out.println(student1); // Student{name='张三', age=18}
System.out.println(student2); // Student{name='李四', age=20}

// 获取成员变量的值
String s1 = (String) name.get(student1);
String s2 = (String) name.get(student2);
System.out.println(s1); // 张三
System.out.println(s2); // 李四
}
}

反射建议:禁止暴力反射操作

反射类中的成员方法

方法 功能
Method[] getMethods() 返回所有公共成员方法对象的数组,包括继承
Method[] getDeclaredMethods() 返回所有成员方法对象的数组,不包括继承
Method getMethod(String name, Class<?>… parameterTypes) 返回单个公共成员方法对象
Method getDeclaredMethod(String name, Class<?>… parameterTypes) 返回单个成员方法对象
  • Method类用于执行方法的方法
方法 功能
Object invoke(Object obj, Object… args) 运行方法

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

public class ReflectDemo06 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<Zi> class1 = Zi.class;

// 获取成员方法列表
Method[] methods1 = class1.getMethods();
System.out.println(Arrays.toString(methods1)); // 很长的一条数组,因为继承了Fu和Object,所以会获取到它们的所有方法

Method[] methods2 = class1.getDeclaredMethods();
System.out.println(Arrays.toString(methods2)); // [public void reflect.Zi.count(int[]), private void reflect.Zi.printHello(double), private void reflect.Zi.printHello(int)]

// 获取指定成员方法
Method count = class1.getMethod("count", int[].class); // 第二个以及以后的参数用于区分重载方法
Zi zi = class1.getConstructor().newInstance(); // 创建调用方法的对象

count.invoke(zi,new int[]{1,2,3,4,5}); // 调用方法
}
}

class Fu {
public void show() {
System.out.println("Fu Show ...");
}
}

class Zi extends Fu{
public Zi() {};

public void count(int... nums) {
int cnt = 0;
for (int i = 0; i < nums.length; i++) {
cnt += nums[i];
}
System.out.println(cnt);
}

private void printHello(int i) {
System.out.println("Hello");
}

private void printHello(double i) {
System.out.println("Hello");
}
}

反射练习

伪泛型

需求:请向一个Integer的集合,添加一个String字符串

思路:Java中的泛型是假的,只在编译的时候有效

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;

public class ReflectTest01 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,1,2,3,4,5);

Class<? extends ArrayList> class1 = list.getClass();
Method add = class1.getMethod("add", Object.class);
add.invoke(list,"你好");

System.out.println(list); // [1, 2, 3, 4, 5, 你好]
}
}

综合练习

需求:

编写三个任意的Javabean类:Student、Teacher、Worker

属性均为name、age

方法要求:

  1. Student里面写学习study和吃饭eat的方法(方法无参返回)
  2. Teacher里面写上课teach和吃饭eat的方法(方法无参返回)
  3. Worker里面写工作work和睡觉sleep的方法

本地新建配置文件properties

属性:className=Student类的全类名,methodName=study

在测试类中读取properties文件中的两个属性值

利用反射创建学生类的对象,并调用方法

修改配置文件,不修改代码,运行老师类中的上课方法

修改配置文件,不修改代码,运行工人类中的工作方法

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package reflect;

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;

public class Test {
public static void main(String[] args) throws Exception {
Properties prop = new Properties();

try (FileInputStream fis = new FileInputStream("Day06\\config.properties")) {
prop.load(fis);
} catch (IOException e) {
e.printStackTrace();
}

// 获取字节码对象
Class<?> class1 = Class.forName(prop.getProperty("className"));
// 创建对象
Object o = class1.
getConstructor(String.class,int.class).
newInstance(prop.getProperty("name"),Integer.parseInt(prop.getProperty("age")));
// 获取成员方法对象
Method method = class1.getMethod(prop.getProperty("methodName"));
// 调用方法
method.invoke(o);
}
}

config.properties:

1
2
3
4
className=reflect.Worker
methodName=work
name=Alice
age=28

注解

  • Annotation表示注解,是JDK1.5的新特性。主要作用:对程序进行标注
  • 理解:注释是给人看的,注解是给JVM虚拟机看的。通过注解可以给类增加额外的信息。编译器或JVM可以根据注解来完成对应的功能。

JDK中常见的注解:

  • @Override:表示方法的重写
  • @Deprecated:表示修饰的方法已经过时
  • @SuppressWarning(“all”):压制警告

注解常用位置:

  1. 方法

自定义注解

  • 自定义注解单独存在意义不大,一般会跟反射结合起来使用
1
2
3
4
5
6
7
8
9
// 格式
public @interface 注解名称 {
public 属性类型 属性名() default 默认值;
}

// 示例
public @interface Anno {
String show() default "show...";
}

属性类型:

  • 基本数据类型
  • String
  • Class
  • 注解
  • 枚举
  • 以上类型的一维数组

Snipaste_2025-08-01_23-00-39

当属性不存在默认值时,使用注解需要指定属性值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package anno;

@MyAnno02(show = "Hello")
public class MyAnnoDemo01 {
@MyAnno02(show = "World",test = "CBA")
public static void main(String[] args) {
method();
}

@MyAnno03("Test") // 如果属性名为value,可以省略
public static void method() {
System.out.println("method");
}
}

@interface MyAnno02 {
String show();
String test() default "ABC";
}

@interface MyAnno03 {
String value();
}
  • 在使用注解时,如果注解的属性没有给出默认值,需要手动给出
  • @Anno(name=”张三”)
  • 如果数组中只有一个属性值,在使用时{}是可以省略的
  • 如果只有一个属性名字为value没有赋值,使用时直接给出值,不需要写属性名

自定义测试注解

需求:自定义一个@Test,用于指定类的方法上。如果类中的某个的方法上使用了该注解,就执行该方法

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package anno;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MyTestDemo01 {
public static void main(String[] args) throws Exception {
Class<MethodDemo> clazz = MethodDemo.class;

MethodDemo methodDemo = clazz.getConstructor().newInstance();

// 获取该类中所有成员方法对象
Method[] methods = clazz.getMethods();
for (Method method : methods) {
// 判断该方法是否存在注解
if (method.isAnnotationPresent(Test.class)) {
method.invoke(methodDemo);
}
}
}
}

@interface Test {}

class MethodDemo {
public MethodDemo() {};
@Test
public void show() {
System.out.println("show");
}

@Test
public void print() {
System.out.println("print");
}

public void method() {
System.out.println("method");
}
}

测试类代码没有问题,但是执行后没有调用show和print方法

主要是因为注解存在生命周期:源代码阶段、字节码阶段、运行阶段,默认是处于字节码阶段

但是反射是在运行阶段的进行操作,所以无法正确识别到注解

isAnnotationPresent():查看是否存在对应注解,参数为注解的字节码对象

元注解

  • 元注解就是用在注解上的注解
元注解名 说明
@Target 指定了注解能在哪里使用
@Retention 可以理解为保留时间(生命周期)

@Target:用来标识注解使用的位置,如果没有使用该注解表示,则自定义的注解可以使用在任意位置

Snipaste_2025-08-01_23-27-36

@Retention:用来标识注解的生命周期(有效范围)

Snipaste_2025-08-01_23-31-06

自定义测试注解案例进行修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package anno;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MyTestDemo01 {
public static void main(String[] args) throws Exception {
Class<MethodDemo> clazz = MethodDemo.class;

MethodDemo methodDemo = clazz.getConstructor().newInstance();

// 获取该类中所有成员方法对象
Method[] methods = clazz.getMethods();
for (Method method : methods) {
// 判断该方法是否存在注解
if (method.isAnnotationPresent(Test.class)) {
method.invoke(methodDemo);
}
}
}
}

@Retention(RetentionPolicy.RUNTIME)
@interface Test {}

class MethodDemo {
public MethodDemo() {};
@Test
public void show() {
System.out.println("show");
}

@Test
public void print() {
System.out.println("print");
}

public void method() {
System.out.println("method");
}
}

现在就可以正确调用show和print方法了

动态代理

  • 动态代理是对象的另外一种创建方式,这种方式可以在不修改源码的情况下,增强方法
  • 动态代理就是在运行时生成代理类,无需手动为每个目标类编写代理类
  • java.lang.reflect.Proxy类:提供了使用动态代理方式创建对象的API
方法 说明
static Object newProxyInstance(
ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h
)
参数一:
用于指定用哪个类加载器,去加载生成的实现类
参数二:
指定接口,这些接口用于指定生成的实现类中有什么 也就是有哪些方法
参数三:
用来指定生成的实现类中不同方法的实现方案

如果目标类的方法不是通过接口实现的,JDK动态代理(Proxy.newProxyInstance)无法直接使用

代理

类似于通过一个中间类对源方法进行增强

Snipaste_2025-08-01_23-43-05

SaleImpl这个类就是中间类(代理类)

不过每当有用户需要进行新的方法增强时,就得重新编写另一份专门的代理类

这种代理方式被称为静态代理

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

@SuppressWarnings("all")
public class ProxyDemo01 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();

// 参数一:应用程序加载器
// 参数二:List字节码对象数组
// 参数三:对具体方法的增强方案
List<Integer> o = (List<Integer>) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{List.class}, new InvocationHandler() {
// proxy:生成的代理对象,一般不直接在内部使用
// method:所有代理的方法
// args:调用方法的时候,传入的实际参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
long begin = 0, end = 0;
if ("add".equals(name)) {
begin = System.currentTimeMillis();
Object invoke = method.invoke(list, args);
end = System.currentTimeMillis();
System.out.println("耗时:" + (end - begin));
return invoke;
}
return null;
}
});

o.add(111);
o.add(222);
o.add(333);

System.out.println(list);
}

public static void showClassLoader() {
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader); // jdk.internal.loader.ClassLoaders$AppClassLoader@36baf30c
}
}

实现动态代理的步骤较为复杂,不需要会熟练使用,但一定要认真理解

但在Spring框架中,有面向切面编程(AOP),其中已经对动态代理进行了封装

小结

  • 动态代理是对象的另外一种创建方式,这种方式可以在不修改源码的情况下,增强方法
  • 代理对象可以在原有功能的基础上进行增强
  • 动态代理在运行时动态的生成代理类,无需手动为每个目标类编写代理类
  • 使用Proxy中的newProxyInstance()方法创建代理对象