Java 序列化深入解析
                           
天天向上
发布: 2025-03-03 23:09:20

原创
89 人浏览过

Java 序列化(Serialization) 是将对象的状态转换为 字节流 以便存储或传输的机制,反序列化(Deserialization)则是将字节流恢复为对象。Java 提供了 SerializableExternalizable 两种方式来实现序列化。


1. Java 序列化的基本概念

序列化作用:

  • 持久化存储(如将对象存入文件或数据库)
  • 网络传输(如远程调用 RMI、WebService、RPC 等)
  • 缓存机制(如 Redis 缓存对象)
  • 深拷贝(通过序列化和反序列化来实现对象的完全复制)

如何序列化?

  • Java 提供 ObjectOutputStream 用于序列化对象
  • ObjectInputStream 用于反序列化对象
  • 需要实现 SerializableExternalizable 接口

2. 使用 Serializable 进行序列化

Serializable 是一个标记接口,没有方法,只需实现它即可使类支持序列化。

2.1 简单示例

import java.io.*;

// 实现 Serializable 接口
class Person implements Serializable {
    private static final long serialVersionUID = 1L; // 序列化版本号
    private String name;
    private int age;

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

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

public class SerializationTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Person person = new Person("Alice", 25);

        // 序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"));
        oos.writeObject(person);
        oos.close();

        // 反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"));
        Person deserializedPerson = (Person) ois.readObject();
        ois.close();

        System.out.println("反序列化后的对象: " + deserializedPerson);
    }
}

2.2 serialVersionUID 的作用

  • serialVersionUID 用于标识类的版本,避免类结构变更后反序列化失败
  • 如果未指定 serialVersionUID,Java 默认生成,但若类结构改变,则可能导致 InvalidClassException
  • 建议显式定义 serialVersionUID 以确保兼容性。
private static final long serialVersionUID = 1L;

3. Java 序列化的注意事项

3.1 transient 关键字

transient 关键字用于阻止字段被序列化,例如:

class Account implements Serializable {
    private static final long serialVersionUID = 1L;
    private String username;
    private transient String password; // 不会被序列化

    public Account(String username, String password) {
        this.username = username;
        this.password = password;
    }

    @Override
    public String toString() {
        return "Account{username='" + username + "', password='" + password + "'}";
    }
}

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Account account = new Account("user1", "secret");

        // 序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("account.ser"));
        oos.writeObject(account);
        oos.close();

        // 反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("account.ser"));
        Account deserializedAccount = (Account) ois.readObject();
        ois.close();

        System.out.println("反序列化后的对象: " + deserializedAccount);
    }
}

输出结果(注意 password 字段被丢弃):

反序列化后的对象: Account{username='user1', password='null'}

3.2 static 关键字

static 变量不会被序列化,因为它属于而不是对象。

class TestStatic implements Serializable {
    private static final long serialVersionUID = 1L;
    static int staticVar = 100;
}

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        TestStatic obj = new TestStatic();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.ser"));
        oos.writeObject(obj);
        oos.close();

        // 修改静态变量
        TestStatic.staticVar = 200;

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test.ser"));
        TestStatic deserializedObj = (TestStatic) ois.readObject();
        ois.close();

        System.out.println("反序列化后的 staticVar: " + TestStatic.staticVar); // 200
    }
}

即使反序列化,静态变量仍保持修改后的值(200),不会恢复为 100。


4. 使用 Externalizable 进行自定义序列化

Externalizable 继承自 Serializable,但要求手动实现 writeExternal()readExternal() 方法

4.1 Externalizable 示例

import java.io.*;

class User implements Externalizable {
    private String username;
    private String password; // 需要加密存储

    public User() {} // 必须提供无参构造方法

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(username);
        out.writeObject(encrypt(password)); // 加密存储
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        username = (String) in.readObject();
        password = decrypt((String) in.readObject()); // 解密
    }

    private String encrypt(String data) {
        return new StringBuilder(data).reverse().toString(); // 简单反转加密
    }

    private String decrypt(String data) {
        return new StringBuilder(data).reverse().toString(); // 反转解密
    }

    @Override
    public String toString() {
        return "User{username='" + username + "', password='" + password + "'}";
    }
}

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        User user = new User("admin", "12345");

        // 序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"));
        oos.writeObject(user);
        oos.close();

        // 反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"));
        User deserializedUser = (User) ois.readObject();
        ois.close();

        System.out.println("反序列化后的对象: " + deserializedUser);
    }
}

Externalizable 适用于:

  • 需要自定义序列化格式
  • 需要加密/解密敏感数据
  • 需要提高序列化性能

5. 序列化的常见问题

5.1 NotSerializableException

如果一个对象没有实现 Serializable,序列化时会抛出:

java.io.NotSerializableException: ClassName

解决方案:让类实现 SerializableExternalizable


5.2 版本兼容性问题

如果类的结构发生改变,反序列化可能会失败:

InvalidClassException: local class incompatible: stream classdesc serialVersionUID = 1L, local class serialVersionUID = 2L

解决方案:手动维护 serialVersionUID


总结

方式适用场景主要特点
Serializable普通对象存储和传输自动序列化,简单易用
Externalizable需要自定义序列化逻辑需要实现 writeExternal()readExternal()

更多详细内容请关注其他相关文章。

发表回复 0

Your email address will not be published. Required fields are marked *