当前位置: 首页 > news >正文

Java 序列化和反序列化为什么要实现Serializable接口

1. 什么是序列化和反序列化

  • 序列化:将对象的状态信息转换为可以存储或传输的形式(通常是字节序列)的过程。例如,将一个 Java 对象保存到文件中或者通过网络发送给其他程序。

  • 反序列化:将字节序列恢复为对象的过程。比如,从文件中读取字节序列并将其转换为 Java 对象。

2. 为什么要实现 Serializable 接口

Serializable 接口是一个标记接口,它本身不包含任何方法。其作用是告诉 Java 虚拟机(JVM)该类的对象可以被序列化。当一个类实现了 Serializable 接口,就相当于给这个类打上了一个 “可以被序列化” 的标记。JVM 在进行序列化操作时,会检查对象所属的类是否实现了 Serializable 接口,如果没有实现,就会抛出 NotSerializableException 异常。

示例代码:

import java.io.*;

// 实现 Serializable 接口
class Person implements Serializable {
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

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

        // 序列化
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
            oos.writeObject(person);
            System.out.println("对象已序列化");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 反序列化
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
            Person deserializedPerson = (Person) ois.readObject();
            System.out.println("对象已反序列化");
            System.out.println("Name: " + deserializedPerson.getName());
            System.out.println("Age: " + deserializedPerson.getAge());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

代码中,Person 类实现了 Serializable 接口,因此可以对其对象进行序列化和反序列化操作。

3. 除了Serializable接口,还有哪些方式可以控制Java对象的序列化?

1. 使用 transient 关键字

transient 关键字用于修饰类的成员变量,被 transient 修饰的变量在序列化过程中会被忽略,即不会被保存到字节流中。在反序列化时,这些变量会被赋予默认值(如 null 、0 等)。

示例代码

import java.io.*;

class User implements Serializable {
    private String username;
    private transient String password;

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

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }
}

public class TransientExample {
    public static void main(String[] args) {
        User user = new User("admin", "123456");
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"));
             ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"))) {
            oos.writeObject(user);
            User deserializedUser = (User) ois.readObject();
            System.out.println("Username: " + deserializedUser.getUsername());
            System.out.println("Password: " + deserializedUser.getPassword());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
2. 使用 Externalizable 接口

Externalizable 接口继承自 Serializable 接口,它要求实现类必须重写 writeExternal 和 readExternal 方法,通过这两个方法可以完全自定义对象的序列化和反序列化过程。

import java.io.*;

class Book implements Externalizable {
    private String title;
    private int year;

    // 必须有一个无参构造函数
    public Book() {}

    public Book(String title, int year) {
        this.title = title;
        this.year = year;
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(title);
        out.writeInt(year);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        title = (String) in.readObject();
        year = in.readInt();
    }

    public String getTitle() {
        return title;
    }

    public int getYear() {
        return year;
    }
}

public class ExternalizableExample {
    public static void main(String[] args) {
        Book book = new Book("Java Programming", 2023);
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("book.ser"));
             ObjectInputStream ois = new ObjectInputStream(new FileInputStream("book.ser"))) {
            oos.writeObject(book);
            Book deserializedBook = (Book) ois.readObject();
            System.out.println(deserializedBook.getTitle() + " - " + deserializedBook.getYear());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
3. 自定义 writeObject 和 readObject 方法

在实现 Serializable 接口的类中,可以定义私有方法 writeObject 和 readObject 来定制序列化和反序列化的逻辑。当 Java 进行序列化和反序列化操作时,会自动调用这两个方法。

import java.io.*;

class Product implements Serializable {
    private String name;
    private double price;

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

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        // 可以添加额外的序列化逻辑
        out.writeDouble(price * 0.9); // 序列化打折后的价格
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        // 可以添加额外的反序列化逻辑
        price = in.readDouble();
    }

    public String getName() {
        return name;
    }

    public double getPrice() {
        return price;
    }
}

public class CustomSerializationExample {
    public static void main(String[] args) {
        Product product = new Product("Laptop", 1000);
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("product.ser"));
             ObjectInputStream ois = new ObjectInputStream(new FileInputStream("product.ser"))) {
            oos.writeObject(product);
            Product deserializedProduct = (Product) ois.readObject();
            System.out.println(deserializedProduct.getName() + " - $" + deserializedProduct.getPrice());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
4. 使用 writeReplace 和 readResolve 方法

writeReplace 方法允许在序列化之前替换要序列化的对象,而 readResolve 方法允许在反序列化之后替换反序列化得到的对象。这两个方法可以用于实现单例模式的序列化和反序列化,确保单例的唯一性。

import java.io.*;

class Singleton implements Serializable {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return INSTANCE;
    }

    private Object writeReplace() throws ObjectStreamException {
        return INSTANCE;
    }

    private Object readResolve() throws ObjectStreamException {
        return INSTANCE;
    }
}

public class ReplaceExample {
    public static void main(String[] args) {
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton.ser"));
             ObjectInputStream ois = new ObjectInputStream(new FileInputStream("singleton.ser"))) {
            oos.writeObject(Singleton.getInstance());
            Singleton deserializedSingleton = (Singleton) ois.readObject();
            System.out.println(Singleton.getInstance() == deserializedSingleton); // 输出 true
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

这些方式可以根据不同的需求灵活控制 Java 对象的序列化过程,以满足安全性、性能等方面的要求。

相关文章:

  • Redis存数据就像存钱:RDB定期存款 vs AOF实时记账
  • 计算机视觉图像点运算【灰度直方图均衡化图形界面实操理解 +开源代码】
  • 深度学习 模型和代码
  • 【经验】Ubuntu|VMware 新建虚拟机后打开 SSH 服务、在主机上安装vscode并连接、配置 git 的 ssh
  • Spring Security的作用
  • 为你的python程序上锁:软件序列号生成器
  • 来看两篇RAG相关的优化工作:多跳查询的优化L-RAG以及利用记忆增强的查询重构MemQ框架
  • 大语言模型打卡学习DAY1
  • 【数据结构C语言】一、基本概念
  • java-正则表达式
  • 【Hadoop】Hadoop是什么?
  • 简单易懂Modbus Tcp和Rtu的异同点
  • AI重构私域增长:从流量收割到终身价值运营的三阶跃迁
  • UI自动化:poium测试库
  • 网络安全系统集成
  • 从Swish到SwiGLU:激活函数的进化与革命,qwen2.5应用的激活函数
  • pythonSTL---os
  • Spring Boot 启动失败:Failed to start bean ‘documentationPluginsBootstrapper’ 解决方案
  • 在 Linux 中,lsblk 命令输出内容解释
  • Linux网络编程——TCP网络通信多线程处理
  • 98岁动物学家、北京大学教授杨安峰逝世
  • 泉州一家婚介机构广告牌越南新娘七天闪婚领证?市监部门介入
  • 上海加力提速推进优化营商环境,明确“十大攻坚突破任务”
  • A股26家游戏企业去年营收近1900亿元:过半净利下滑,出海成为主流选择
  • 明查|这是“C919迫降在农田”?实为飞机模型将用于科普体验
  • 戴维·珀杜宣誓就任美国驻华大使