面试题:简述 Java 序列化中如果有些字段不想进行序列化,怎么办 ?

在 Java 中,序列化是将对象转换为字节流的过程,反序列化则是将字节流恢复为对象的过程。默认情况下,对象的非静态和非瞬态字段都会被序列化。如果某些字段不需要序列化,可以通过以下方式实现:


1. 使用 transient 关键字

  • 作用transient 关键字用于标记字段,使其在序列化时被忽略。
  • 适用场景: 适用于不需要持久化或传输的敏感数据(如密码)或临时数据。
  • 示例代码:
import java.io.Serializable;

public class User implements Serializable {
    private String username;
    private transient String password; // 不会被序列化

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

    @Override
    public String toString() {
        return "User{username='" + username + "', password='" + password + "'}";
    }
}
  • 测试代码:
import java.io.*;

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

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

        // 反序列化
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"))) {
            User deserializedUser = (User) ois.readObject();
            System.out.println(deserializedUser); // 输出: User{username='admin', password='null'}
        }
    }
}
  • 输出结果:
User{username='admin', password='null'}
  • 说明password 字段被标记为 transient,因此在反序列化后其值为 null

2. 自定义序列化(实现 writeObject 和 readObject 方法)

  • 作用: 通过实现 writeObject 和 readObject 方法,可以完全控制序列化和反序列化的过程。
  • 适用场景: 需要更灵活地控制序列化行为时。
  • 示例代码:
import java.io.*;

public class CustomSerializationUser implements Serializable {
    private String username;
    private String password;

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

    private void writeObject(ObjectOutputStream oos) throws IOException {
        oos.defaultWriteObject(); // 默认序列化
        oos.writeObject("encrypted_" + password); // 自定义序列化逻辑
    }

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject(); // 默认反序列化
        password = ((String) ois.readObject()).replace("encrypted_", ""); // 自定义反序列化逻辑
    }

    @Override
    public String toString() {
        return "CustomSerializationUser{username='" + username + "', password='" + password + "'}";
    }
}
  • 测试代码:
public class CustomSerializationExample {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        CustomSerializationUser user = new CustomSerializationUser("admin", "123456");

        // 序列化
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("custom_user.ser"))) {
            oos.writeObject(user);
        }

        // 反序列化
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("custom_user.ser"))) {
            CustomSerializationUser deserializedUser = (CustomSerializationUser) ois.readObject();
            System.out.println(deserializedUser); // 输出: CustomSerializationUser{username='admin', password='123456'}
        }
    }
}
  • 输出结果:
CustomSerializationUser{username='admin', password='123456'}
  • 说明: 通过自定义 writeObject 和 readObject 方法,可以在序列化时加密字段,在反序列化时解密字段。

3. 使用 static 字段

  • 作用static 字段属于类而非对象,因此不会被序列化。
  • 适用场景: 适用于与对象状态无关的字段。
  • 示例代码:
import java.io.Serializable;

public class StaticFieldUser implements Serializable {
    private String username;
    private static String password = "default"; // 不会被序列化

    public StaticFieldUser(String username) {
        this.username = username;
    }

    @Override
    public String toString() {
        return "StaticFieldUser{username='" + username + "', password='" + password + "'}";
    }
}
  • 测试代码:
import java.io.*;

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

        // 序列化
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("static_user.ser"))) {
            oos.writeObject(user);
        }

        // 反序列化
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("static_user.ser"))) {
            StaticFieldUser deserializedUser = (StaticFieldUser) ois.readObject();
            System.out.println(deserializedUser); // 输出: StaticFieldUser{username='admin', password='default'}
        }
    }
}
  • 说明password 是静态字段,不会被序列化,反序列化后其值保持不变。

总结

方法适用场景
transient 关键字简单场景,直接忽略字段的序列化。
自定义 writeObject 和 readObject复杂场景,需要完全控制序列化和反序列化行为。
static 字段字段与对象状态无关,且不需要序列化。

根据具体需求选择合适的方式,可以有效控制序列化行为。

THE END
点赞10 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容