背景

本文是《Java 后端从小白到大神》修仙系列第十六篇,正式进入Java后端世界,本篇文章主要聊Java基础中的反射与序列化。若想详细学习请点击 首篇博文,我们开始吧。

文章概览

  1. 反射机制详解
  2. 序列化与反序列化
  3. 反射与序列化的结合应用
  4. 代码示例与最佳实践

一、反射机制详解

1.1 什么是反射?

反射(Reflection) 是 Java 提供的一种能力,允许程序在运行时动态获取类的信息(如类名、方法、字段、注解等)并操作类或对象。

核心价值

  • 动态性:突破编译时的类型限制
  • 灵活性:运行时决定调用哪个方法
  • 元编程:操作类的元数据

1.2 反射的核心类

类名 功能 主要方法
Class<T> 类的元数据入口 forName(), getConstructor(), getMethod(), getField()
Constructor<T> 构造器 newInstance(), setAccessible()
Method 方法 invoke(), setAccessible()
Field 字段 get(), set(), setAccessible()
Annotation 注解 annotationType(), value()
Modifier 修饰符 isPublic(), isPrivate(), isStatic()

1.3 获取 Class 对象的方式

同一个类,在 JVM 里只有一个 Class 对象,所以三种方式拿到的都是同一个对象。

  1. 通过 类名.class 获取(最安全、最常用)
1
2
3
// 编译期就能确定,不会抛出异常
Class<?> clazz1 = String.class;
System.out.println("方式1: " + clazz1.getName());
  1. 通过 对象.getClass() 获取
1
2
3
4
// 必须先有实例对象,才能调用
String str = "Hello";
Class<?> clazz2 = str.getClass();
System.out.println("方式2: " + clazz2.getName());
  1. 通过 Class.forName(全类名) 获取(反射常用)
1
2
3
// 运行时动态加载,需要写完整包名+类名,会抛出检查异常
Class<?> clazz3 = Class.forName("java.lang.String");
System.out.println("方式3: " + clazz3.getName());
  1. 三个 Class 对象是同一个
1
2
3
// 同一个类在 JVM 中只有一个 Class 对象
System.out.println("clazz1 == clazz2: " + (clazz1 == clazz2));  // true
System.out.println("clazz1 == clazz3: " + (clazz1 == clazz3));  // true

1.4 创建对象实例

先拿到 Class 对象,再通过反射创建实例。

1
Class<?> stringClass = String.class;
  1. 使用无参构造器
1
2
3
4
5
// Class.newInstance() 在 Java9+ 已过时
// 底层调用 无参构造
@SuppressWarnings("deprecation")
String str1 = (String) stringClass.newInstance();
System.out.println("无参构造器: " + str1);
  1. 使用带参构造器
1
2
3
4
5
6
// 获取指定参数类型的构造器. String(String original)
Constructor<?> constructor = stringClass.getConstructor(String.class);

// 通过构造器创建对象
String str2 = (String) constructor.newInstance("Hello, Reflection!");
System.out.println("带参构造器: " + str2);
  1. 其他类带参构造示例
1
2
3
4
5
6
// 获取 Integer 的 String 参数构造器
Constructor<?> intConstructor = Integer.class.getConstructor(String.class);

// 创建 Integer 实例
Integer num = (Integer) intConstructor.newInstance("123");
System.out.println("带参构造创建Integer: " + num);

1.5 调用方法

  1. 调用静态方法
    静态方法调用时,invoke 第一个参数传 null
1
2
3
4
5
6
7
8
9
// 获取 Class 对象
Class<?> mathClass = Math.class;

// 获取方法:方法名 + 参数类型
Method sqrtMethod = mathClass.getDeclaredMethod("sqrt", double.class);

// 调用:invoke(对象, 参数) → 静态方法对象为 null
Object result1 = sqrtMethod.invoke(null, 25.0);
System.out.println("Math.sqrt(25.0): " + result1);
  1. 调用实例方法
    必须传入具体对象,才能调用实例方法
1
2
3
4
5
6
7
8
String str = "Hello";

// 获取 substring(int, int) 方法
Method substringMethod = String.class.getDeclaredMethod("substring", int.class, int.class);

// 调用:invoke(对象, 起始索引, 结束索引)
Object result2 = substringMethod.invoke(str, 1, 4);
System.out.println("str.substring(1, 4): " + result2);
  1. 调用私有方法
    必须先 setAccessible(true) 打破访问权限
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Person person = new Person("Alice");

// 获取私有方法
Method privateMethod = Person.class.getDeclaredMethod("privateMethod");

// 开启权限访问
privateMethod.setAccessible(true);

// 调用私有方法
Object result3 = privateMethod.invoke(person);
System.out.println("私有方法调用: " + result3);
  1. 内部类 Person(用于演示私有方法)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
static class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    private String privateMethod() {
        return "Private method called by " + name;
    }
}

1.6 操作字段

  1. 先创建对象
    类 Person
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
static class Person {
    public String name;
    private int age;
    public static String staticField = "Static Value";

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
1
Person person = new Person("Alice", 20);
  1. 获取并修改公有字段
1
2
3
4
5
6
7
8
9
// 获取公有字段 name
Field nameField = Person.class.getDeclaredField("name");

// 获取字段值
System.out.println("原始 name: " + nameField.get(person));

// 修改字段值
nameField.set(person, "Bob");
System.out.println("修改后 name: " + nameField.get(person));
  1. 获取并修改私有字段
    私有字段必须先开启权限:setAccessible(true)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 获取私有字段 age
Field ageField = Person.class.getDeclaredField("age");

// 打破访问检查
ageField.setAccessible(true);

// 获取并修改
System.out.println("原始 age: " + ageField.get(person));
ageField.set(person, 25);
System.out.println("修改后 age: " + ageField.get(person));
  1. 操作静态字段
    静态字段属于类,get/set 时对象传 null
1
2
Field staticField = Person.class.getDeclaredField("staticField");
System.out.println("静态字段: " + staticField.get(null));

1.7 反射的性能优化

 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
/**
 * 反射性能优化示例
 */
public class ReflectionPerformanceDemo {
    // 缓存 Class 对象
    private static final Class<?> STRING_CLASS = String.class;
    // 缓存方法对象
    private static Method concatMethod;
    
    static {
        try {
            // 在静态初始化块中获取方法,避免重复查找
            concatMethod = STRING_CLASS.getDeclaredMethod("concat", String.class);
            concatMethod.setAccessible(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) throws Exception {
        long start = System.nanoTime();
        
        String str = "Hello";
        for (int i = 0; i < 100000; i++) {
            // 直接调用(基准测试)
            // str.concat(" World");
            
            // 反射调用(优化后)
            concatMethod.invoke(str, " World");
        }
        
        long end = System.nanoTime();
        System.out.println("耗时: " + (end - start) / 1000000 + " ms");
    }
}

二、序列化与反序列化

2.1 什么是序列化?

序列化是将对象转换为字节流的过程,用于:

  • 网络传输(如 RPC、Socket 通信)
  • 持久化存储(如文件、数据库 BLOB)
  • 进程间通信

反序列化是将字节流恢复为对象的过程。


序列化中 JVM 自动调用的方法清单

  1. writeObject() —— 序列化时自动调用
1
private void writeObject(ObjectOutputStream oos) throws IOException

触发时机oos.writeObject(obj)
作用:自定义序列化逻辑

  1. readObject() —— 反序列化时自动调用
1
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException

触发时机ois.readObject()
作用:自定义反序列化逻辑

  1. writeReplace() —— 序列化前自动替换对象
1
private Object writeReplace() throws ObjectStreamException

触发时机writeObject 之前
作用:返回一个“替身对象”进行序列化(序列化代理模式核心)

  1. readResolve() —— 反序列化后自动替换对象
1
private Object readResolve() throws ObjectStreamException

触发时机readObject 之后
作用:返回最终真正的对象(单例/代理模式必用)

  1. writeExternal() —— Externalizable 序列化
1
public void writeExternal(ObjectOutput out) throws IOException

触发时机:序列化时
作用:手动控制序列化字段

  1. readExternal() —— Externalizable 反序列化
1
public void readExternal(ObjectInput in) throws IOException

触发时机:反序列化时
作用:手动控制反序列化字段

2.2 核心接口与类

接口/类 功能 主要方法
Serializable 标记接口,表示类可序列化 无方法,仅作标记
Externalizable 扩展序列化接口,需手动实现 writeExternal(), readExternal()
ObjectOutputStream 序列化输出流 writeObject(), writeInt()
ObjectInputStream 反序列化输入流 readObject(), readInt()
transient 关键字,标记字段不参与序列化 -
serialVersionUID 序列化版本号 静态常量

2.3 基本序列化

 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
/**
 * 基本序列化示例
 */
public class BasicSerializationDemo {
    public static void main(String[] args) {
        // 序列化
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("person.ser"))) { // .ser = 序列化文件
            Person person = new Person("Alice", 20, "password123");
            oos.writeObject(person);
            System.out.println("序列化完成");
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 反序列化
        try (ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("person.ser"))) {
            Person person = (Person) ois.readObject();
            System.out.println("反序列化完成");
            System.out.println("Name: " + person.getName());
            System.out.println("Age: " + person.getAge());
            System.out.println("Password: " + person.getPassword()); // null(transient 字段)
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    static class Person implements Serializable {
        private static final long serialVersionUID = 1L; // 显式定义版本号
        
        private String name;
        private int age;
        private transient String password; // 敏感数据不序列化
        
        public Person(String name, int age, String password) {
            this.name = name;
            this.age = age;
            this.password = password;
        }
        
        // Getters (略)
        public String getName() { return name; }
        public int getAge() { return age; }
        public String getPassword() { return password; }
    }
}

2.4 自定义序列化

自定义序列化的 writeObject / readObject不是你手动调用的!是 JVM 底层自动调用的!

1
2
private void writeObject(ObjectOutputStream oos) throws IOException { ... }
private void readObject(ObjectInputStream ois) throws IOException { ... }

它们是:固定签名的钩子方法(Hook)

  • 权限必须是 private
  • 方法名必须是 writeObject / readObject
  • 参数必须固定

只要写了,Java 序列化机制自动识别执行 writeObject() 时会自动调用你写的这个方法!

底层实际发生:

1
2
3
4
5
6
oos.writeObject(user)
    ↓ (JVM 内部自动走)
判断 user 类有没有自己写 writeObject()
有 → 自动调用你写的那个 private 方法
没有 → 走默认序列化

defaultWriteObject() = 自动序列化非静态、非 transient 字段
writeObject(加密密码) = 手动追加写入加密后的密码

 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
51
52
53
54
55
56
57
58
59
60
61
/**
 * 自定义序列化示例
 */
public class CustomSerializationDemo {
    public static void main(String[] args) {
        // 序列化
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("user.ser"))) {
            User user = new User("Alice", "secret123");
            oos.writeObject(user);
            System.out.println("序列化完成");
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 反序列化
        try (ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("user.ser"))) {
            User user = (User) ois.readObject();
            System.out.println("反序列化完成");
            System.out.println("Username: " + user.getUsername());
            System.out.println("Password: " + user.getPassword());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    static class User implements Serializable {
        private static final long serialVersionUID = 1L;
        
        private String username;
        private String password;
        
        public User(String username, String password) {
            this.username = username;
            this.password = password;
        }
        
        // 自定义序列化方法
        private void writeObject(ObjectOutputStream oos) throws IOException {
            oos.defaultWriteObject(); // 默认序列化
            // 对密码进行简单加密(实际应用中应使用更安全的加密方式)
            String encryptedPassword = password != null ? 
                new StringBuilder(password).reverse().toString() : null;
            oos.writeObject(encryptedPassword);
        }
        
        // 自定义反序列化方法
        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
            ois.defaultReadObject(); // 默认反序列化
            // 解密密码
            String encryptedPassword = (String) ois.readObject();
            password = encryptedPassword != null ? 
                new StringBuilder(encryptedPassword).reverse().toString() : null;
        }
        
        // Getters (略)
        public String getUsername() { return username; }
        public String getPassword() { return password; }
    }
}

2.5 Externalizable 接口

Externalizable 接口提供更细粒度的序列化控制。

Product 实现了 Externalizable 接口
当执行:

1
oos.writeObject(product);

JVM 内部一检查:

哦,这是个 Externalizable 对象 那我自动调用它的 writeExternal()

反序列化时:

1
ois.readObject();

JVM 就自动调用:

1
readExternal()

implements Externalizable
→ 接口强制要求重写 writeExternal / readExternal
→ JVM 直接调用接口方法

本质一样:都是底层自动回调,你不用显式调用。

默认序列化
JVM 全包,你不能插手,不能控制,不能优化。

Externalizable序列化
自己亲手写每一个字段的读写
存什么、怎么存、顺序、加密、过滤、优化 → 全部自己说了算!

这就叫:精细化序列化

 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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import java.io.*;

/**
 * Externalizable 示例
 * 特点:手动精细化控制序列化(自己写每个字段的读写逻辑)
 */
public class ExternalizableDemo {
    public static void main(String[] args) {

        // --------------- 序列化 ---------------
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("product.ser"))) {

            Product product = new Product("Laptop", 9999.99);
            oos.writeObject(product); // JVM 自动调用 writeExternal()
            System.out.println("序列化完成");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // --------------- 反序列化 ---------------
        try (ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("product.ser"))) {

            Product product = (Product) ois.readObject(); // JVM 自动调用 readExternal()
            System.out.println("反序列化完成");
            System.out.println("Name: " + product.getName());
            System.out.println("Price: " + product.getPrice());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 实现 Externalizable 接口
     * 完全由自己手动控制序列化 → 【精细化序列化】
     */
    static class Product implements Externalizable {
        private String name;
        private double price;

        /**
         * 必须有无参构造器!
         * Externalizable 反序列化时强制需要
         */
        public Product() {
        }

        public Product(String name, double price) {
            this.name = name;
            this.price = price;
        }

        /**
         * 【精细化核心】
         * 手动决定:序列化哪些字段、顺序、格式
         * JVM 自动调用,不需要手动写代码调用
         */
        @Override
        public void writeExternal(ObjectOutput out) throws IOException {
            // 手动写字段(自己控制顺序、控制存储内容)
            out.writeUTF(name);
            out.writeDouble(price);
        }

        /**
         * 【精细化核心】
         * 手动决定:怎么读取、读取顺序、如何赋值
         * JVM 自动调用
         */
        @Override
        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            // 必须和 writeExternal 写入顺序完全一致
            name = in.readUTF();
            price = in.readDouble();
        }

        // Getters
        public String getName() { return name; }
        public double getPrice() { return price; }
    }
}

2.6 版本兼容性

serialVersionUID 用于确保序列化版本的兼容性:

  • 未显式定义:JVM 会根据类结构自动生成
  • 显式定义:建议始终显式定义,避免类结构变化导致反序列化失败

版本兼容性规则

  • 修改字段类型:可能导致反序列化失败
  • 新增字段:反序列化时会赋予默认值
  • 删除字段:反序列化时会忽略不存在的字段

2.7 序列化的替代方案

方案 优点 缺点 主流Java工具 & 核心用法
JSON 轻量、易读、跨语言 性能略低,无类型 Jackson / Gson
序列化:toJSONString(obj)
反序列化:parseObject(json)
XML 标准、结构清晰 体积大、解析慢 JAXB / Jackson XML
自动转对象/XML
Protobuf 性能极高、体积极小 学习成本高、二进制 Protobuf Java
自动生成代码,直接 toByteArray()
MessagePack 高性能、体积小 二进制不可读 MessagePack-Java
类似JSON,但更快更小
Avro schema 灵活、兼容强 依赖schema Apache Avro
序列化二进制数据流
  1. JSON(最常用,90%项目都用它)
    工具:Jackson(Spring默认)、Gson
    作用:接口传参、前后端交互、配置文件
1
2
3
4
5
// 序列化
String json = new ObjectMapper().writeValueAsString(user);

// 反序列化
User user = new ObjectMapper().readValue(json, User.class);
  1. XML
    工具:JAXB、Jackson XML
    作用:老项目、接口报文、配置文件
    XML(JAXB 示例)
1
2
3
4
5
6
7
8
// 序列化:对象 → XML
JAXBContext context = JAXBContext.newInstance(User.class);
Marshaller marshaller = context.createMarshaller();
marshaller.marshal(user, new File("user.xml"));

// 反序列化:XML → 对象
Unmarshaller unmarshaller = context.createUnmarshaller();
User user = (User) unmarshaller.unmarshal(new File("user.xml"));
  1. Protobuf(高性能首选)
    工具:Protobuf
    作用:微服务、高性能通信、游戏、大数据
    特点:速度最快、体积最小
1
2
3
4
5
6
7
// 先写 .proto 文件,自动生成 UserProto 类

// 序列化:对象 → byte[]
byte[] data = userProto.toByteArray();

// 反序列化:byte[] → 对象
UserProto user = UserProto.parseFrom(data);
  1. MessagePack
    工具:MessagePack
    作用:高性能二进制JSON
1
2
3
4
5
6
7
ObjectMapper mapper = new ObjectMapper(new MessagePackFactory());

// 序列化
byte[] bytes = mapper.writeValueAsBytes(user);

// 反序列化
User user = mapper.readValue(bytes, User.class);
  1. Avro
    工具:Apache Avro
    作用:大数据、Hadoop、Kafka
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 序列化:对象 → 字节
ByteArrayOutputStream out = new ByteArrayOutputStream();
DatumWriter<User> writer = new SpecificDatumWriter<>(User.class);
Encoder encoder = EncoderFactory.get().binaryEncoder(out, null);
writer.write(user, encoder);
encoder.flush();

// 反序列化:字节 → 对象
DatumReader<User> reader = new SpecificDatumReader<>(User.class);
Decoder decoder = DecoderFactory.get().binaryDecoder(out.toByteArray(), null);
User user = reader.read(null, decoder);

三、反射与序列化的结合应用

3.1 动态对象创建

 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
51
52
53
54
55
56
/**
 * 通过反射和序列化创建动态对象
 */
public class DynamicObjectCreation {
    public static void main(String[] args) throws Exception {
        // 通过反射创建对象
        Class<?> clazz = Class.forName("com.example.User");
        Object obj = clazz.newInstance();
        
        // 通过反射设置字段
        Field nameField = clazz.getDeclaredField("name");
        nameField.setAccessible(true);
        nameField.set(obj, "Alice");
        
        Field ageField = clazz.getDeclaredField("age");
        ageField.setAccessible(true);
        ageField.set(obj, 30);
        
        // 序列化对象
        byte[] serialized = serialize(obj);
        System.out.println("序列化字节长度: " + serialized.length);
        
        // 反序列化对象
        Object deserialized = deserialize(serialized);
        System.out.println("反序列化对象: " + deserialized);
    }
    
    // 序列化方法
    public static byte[] serialize(Object obj) throws IOException {
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(bos)) {
            oos.writeObject(obj);
            return bos.toByteArray();
        }
    }
    
    // 反序列化方法
    public static Object deserialize(byte[] data) throws Exception {
        try (ByteArrayInputStream bis = new ByteArrayInputStream(data);
             ObjectInputStream ois = new ObjectInputStream(bis)) {
            return ois.readObject();
        }
    }
    
    // 示例类
    static class User implements Serializable {
        private static final long serialVersionUID = 1L;
        private String name;
        private int age;
        
        @Override
        public String toString() {
            return "User{name='" + name + "', age=" + age + "}";
        }
    }
}

3.2 通用对象拷贝

序列化只是存数据,反序列化 = JVM 自动 new 一个新对象 + 填数据,所以一定是新对象,一定是深拷贝!

  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
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import java.io.*;

/**
 * 通过序列化实现深拷贝
 * 深拷贝:拷贝出一个完全独立的新对象,修改新对象【不会影响】原对象
 */
public class DeepCopyDemo {
    public static void main(String[] args) throws Exception {
        // 1. 创建原始对象
        Person original = new Person("Alice", 20);
        original.setAddress(new Address("Beijing", "China"));
        System.out.println("原始对象: " + original);

        // 2. 调用深拷贝方法,得到一个【全新的、独立的】Person对象
        Person copy = (Person) deepCopy(original);
        System.out.println("拷贝对象: " + copy);

        // ==========================================
        // 关键测试:修改【拷贝对象】的属性
        // 深拷贝 → 修改 copy 不会影响 original
        // ==========================================
        copy.setName("Bob");                  // 修改拷贝对象的名字
        copy.getAddress().setCity("Shanghai");// 修改拷贝对象的地址城市

        // 输出结果:原始对象 不变!拷贝对象 变了!
        System.out.println("修改后 - 原始对象: " + original);
        System.out.println("修改后 - 拷贝对象: " + copy);
    }

    /**
     * 序列化深拷贝核心方法
     * 原理:
     * 1. 将对象序列化成字节数组(写到内存流)
     * 2. 再从字节数组反序列化为新对象
     * 最终得到一个【完全独立的新对象】
     */
    @SuppressWarnings("unchecked")
    public static <T> T deepCopy(T object) throws Exception {
        if (object == null) {
            return null;
        }

        // 第一步:序列化对象 → 内存字节流
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(bos)) {

            // 将对象写入流(序列化)
            oos.writeObject(object);

            // 第二步:从内存字节流 → 反序列化为新对象
            try (ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
                 ObjectInputStream ois = new ObjectInputStream(bis)) {

                // 反序列化,返回【全新对象】
                return (T) ois.readObject();
            }
        }
    }

    /**
     * 人类
     * 必须实现 Serializable,否则无法序列化
     */
    static class Person implements Serializable {
        private static final long serialVersionUID = 1L;
        
        private String name;
        private int age;
        private Address address; // 引用类型成员

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

        // getter & setter
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        public int getAge() { return age; }
        public void setAge(int age) { this.age = age; }
        public Address getAddress() { return address; }
        public void setAddress(Address address) { this.address = address; }

        @Override
        public String toString() {
            return "Person{name='" + name + "', age=" + age + ", address=" + address + "}";
        }
    }

    /**
     * 地址类
     * 也必须实现 Serializable,因为 Person 引用了它
     */
    static class Address implements Serializable {
        private static final long serialVersionUID = 1L;
        
        private String city;
        private String country;

        public Address(String city, String country) {
            this.city = city;
            this.country = country;
        }

        // getter & setter
        public String getCity() { return city; }
        public void setCity(String city) { this.city = city; }
        public String getCountry() { return country; }
        public void setCountry(String country) { this.country = country; }

        @Override
        public String toString() {
            return "Address{city='" + city + "', country='" + country + "'}";
        }
    }
}

3.3 序列化代理模式

谁的方法,this 就是谁,任何非静态方法:

1
对象.方法();

那么方法内部的 this 永远 == 前面那个 对象
这是 Java 规定死的,不是我编的,不是虚拟机编的。

看下面这段代码,序列化时,JVM 是这样干活的:

1
2
3
4
5
6
7
static class ImmutablePerson implements Serializable {

    // 这是【成员方法】
    private Object writeReplace() throws ObjectStreamException {
        return new SerializationProxy(this); // ← this
    }
}
  1. JVM 拿到 person 对象
  2. JVM 检查:这个 person 有没有 writeReplace() 方法?
  3. 有!
  4. JVM 自己调用:person.writeReplace()
  5. 根据Java 规定死的,所以方法里的 this = person

代码示例整体流程

序列化时:

1
2
3
4
5
6
调用:oos.writeObject(person)
          ↓(JVM 自动检查)
发现 person 里有 writeReplace() 方法
JVM 不序列化 person
而是序列化:writeReplace() 返回的 SerializationProxy

反序列化时:

1
2
3
4
5
6
7
8
9
调用:ois.readObject()
JVM 先读到 SerializationProxy 对象
发现代理类有 readResolve()
调用它 → 返回 new ImmutablePerson(...)
最终你得到的是 ImmutablePerson 对象

代码示例

 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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import java.io.*;
import java.util.Objects;

/**
 * 序列化代理模式
 */
public class SerializationProxyDemo {
    public static void main(String[] args) throws Exception {
        // 1. 创建对象
        ImmutablePerson person = new ImmutablePerson("Alice", 30);
        System.out.println("原始对象: " + person);

        // 2. 序列化(对象 → 字节)
        byte[] data = serialize(person);

        // 3. 反序列化(字节 → 对象)
        ImmutablePerson deserialized = (ImmutablePerson) deserialize(data);
        System.out.println("反序列化对象: " + deserialized);
        System.out.println("对象相等: " + person.equals(deserialized));
    }

    // --------------------- 序列化方法 ---------------------
    // 序列化:对象 → 字节数组
    private static byte[] serialize(Object obj) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(obj);
        return bos.toByteArray();
    }

    // 反序列化:字节数组 → 对象
    private static Object deserialize(byte[] data) throws IOException, ClassNotFoundException {
        ByteArrayInputStream bis = new ByteArrayInputStream(data);
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
    }
    // ----------------------------------------------------------------

    static class ImmutablePerson implements Serializable {
        private static final long serialVersionUID = 1L;
        private final String name;
        private final int age;

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

        // ==================== 核心:序列化代理 ====================
        // 1. 自动被调用:序列化时,不序列化自己,而是序列化【代理对象】
        private Object writeReplace() throws ObjectStreamException {
            return new SerializationProxy(this);
        }

        // 2. 禁止直接反序列化,保护对象安全
        private void readObject(ObjectInputStream stream) throws InvalidObjectException {
            throw new InvalidObjectException("Use SerializationProxy instead");
        }

        public String getName() { return name; }
        public int getAge() { return age; }

        @Override
        public String toString() {
            return "ImmutablePerson{name='" + name + "', age=" + age + "}";
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            ImmutablePerson that = (ImmutablePerson) o;
            return age == that.age && Objects.equals(name, that.name);
        }

        @Override
        public int hashCode() {
            return Objects.hash(name, age);
        }

        // ==================== 真正被序列化的【代理类】 ====================
        // 这个类才是真正被写到字节里的对象
        private static class SerializationProxy implements Serializable {
            private static final long serialVersionUID = 1L;
            private final String name;
            private final String age;

            SerializationProxy(ImmutablePerson person) {
                this.name = person.name;
                this.age = person.age;
            }

            // 反序列化时:用代理对象 → 重建 ImmutablePerson
            private Object readResolve() {
                return new ImmutablePerson(name, age);
            }
        }
    }
}

四、代码示例与最佳实践

4.1 反射工具类

 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
51
52
53
54
55
56
57
58
59
/**
 * 反射工具类
 */
public class ReflectionUtils {
    
    /**
     * 创建对象实例
     */
    public static <T> T instantiate(Class<T> clazz) {
        try {
            return clazz.newInstance();
        } catch (Exception e) {
            throw new RuntimeException("Failed to instantiate class: " + clazz.getName(), e);
        }
    }
    
    /**
     * 调用方法
     */
    public static Object invokeMethod(Object obj, String methodName, Object... args) {
        try {
            Class<?>[] paramTypes = new Class[args.length];
            for (int i = 0; i < args.length; i++) {
                paramTypes[i] = args[i].getClass();
            }
            Method method = obj.getClass().getDeclaredMethod(methodName, paramTypes);
            method.setAccessible(true);
            return method.invoke(obj, args);
        } catch (Exception e) {
            throw new RuntimeException("Failed to invoke method: " + methodName, e);
        }
    }
    
    /**
     * 获取字段值
     */
    public static Object getFieldValue(Object obj, String fieldName) {
        try {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(obj);
        } catch (Exception e) {
            throw new RuntimeException("Failed to get field: " + fieldName, e);
        }
    }
    
    /**
     * 设置字段值
     */
    public static void setFieldValue(Object obj, String fieldName, Object value) {
        try {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(obj, value);
        } catch (Exception e) {
            throw new RuntimeException("Failed to set field: " + fieldName, e);
        }
    }
}

4.2 序列化工具类

 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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
 * 序列化工具类
 */
public class SerializationUtils {
    
    /**
     * 序列化对象为字节数组
     */
    public static byte[] serialize(Object obj) {
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(bos)) {
            oos.writeObject(obj);
            return bos.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException("Serialization failed", e);
        }
    }
    
    /**
     * 从字节数组反序列化对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T deserialize(byte[] data) {
        try (ByteArrayInputStream bis = new ByteArrayInputStream(data);
             ObjectInputStream ois = new ObjectInputStream(bis)) {
            return (T) ois.readObject();
        } catch (Exception e) {
            throw new RuntimeException("Deserialization failed", e);
        }
    }
    
    /**
     * 深拷贝对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T deepCopy(T obj) {
        if (obj == null) {
            return null;
        }
        return (T) deserialize(serialize(obj));
    }
    
    /**
     * 序列化对象到文件
     */
    public static void serializeToFile(Object obj, String filePath) {
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream(filePath))) {
            oos.writeObject(obj);
        } catch (IOException e) {
            throw new RuntimeException("Failed to serialize to file: " + filePath, e);
        }
    }
    
    /**
     * 从文件反序列化对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T deserializeFromFile(String filePath) {
        try (ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream(filePath))) {
            return (T) ois.readObject();
        } catch (Exception e) {
            throw new RuntimeException("Failed to deserialize from file: " + filePath, e);
        }
    }
}

4.3 最佳实践总结

反射最佳实践

  • 优先使用接口和设计模式,避免反射
  • 缓存反射对象,提高性能
  • 仅在必要时使用 setAccessible(true)
  • 处理异常,增强代码健壮性
  • 考虑使用 MethodHandle 替代传统反射

序列化最佳实践

  • 始终显式定义 serialVersionUID
  • 使用 transient 标记敏感或不需要序列化的字段
  • 实现自定义序列化时注意安全
  • 考虑使用 JSON/Protobuf 等替代方案
  • 对反序列化输入进行验证

五、总结

1 性能优化

  • 反射:缓存Class、Method、Field,避免重复获取;少用setAccessible(true),可用MethodHandle提升性能
  • 序列化:用transient忽略无用/敏感字段;优先选JSON/Protobuf,高性能场景用Externalizable

2 安全注意

  • 反射:谨慎使用setAccessible(true),对输入做校验
  • 序列化:不反序列化不可信数据,显式声明serialVersionUID,优先用JSON替代原生序列化

3 最佳实践

  • 非必要不使用反射,反射对象必须缓存
  • 敏感字段用transient,不可变类用序列化代理模式
  • 生产环境优先JSON/Protobuf,完善异常处理

4 应用场景

  • 应用:框架底层(Spring/MyBatis)、RPC、动态配置、数据持久化
  • 提醒:反射/序列化有性能开销、安全风险,需注意兼容性

反射和序列化是 Java 中强大的特性,掌握它们可以编写更加灵活、动态的代码。但同时也需要注意它们的局限性和潜在风险,在实际应用中权衡利弊,合理使用。