• Java之序列化的详细解析


    3. 序列化

    3.1 概述

    Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据对象的类型对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。

    反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化对象的数据对象的类型对象中存储的数据信息,都可以用来在内存中创建对象。看图理解序列化:

    3.2 ObjectOutputStream类

    java.io.ObjectOutputStream 类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。

    构造方法

    • public ObjectOutputStream(OutputStream out): 创建一个指定OutputStream的ObjectOutputStream。

    构造举例,代码如下:

    1. FileOutputStream fileOut = new FileOutputStream("employee.txt");
    2. ObjectOutputStream out = new ObjectOutputStream(fileOut);

    序列化操作

    1. 一个对象要想序列化,必须满足两个条件:

    • 该类必须实现java.io.Serializable 接口,Serializable 是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException

    • 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient 关键字修饰。

    1. public class Employee implements java.io.Serializable {
    2.    public String name;
    3.    public String address;
    4.    public transient int age; // transient瞬态修饰成员,不会被序列化
    5.    public void addressCheck() {
    6.     System.out.println("Address check : " + name + " -- " + address);
    7.   }
    8. }

    2.写出对象方法

    • public final void writeObject (Object obj) : 将指定的对象写出。

    1. public class SerializeDemo{
    2.   public static void main(String [] args)   {
    3.   Employee e = new Employee();
    4.   e.name = "zhangsan";
    5.   e.address = "beiqinglu";
    6.   e.age = 20;
    7.   try {
    8.     // 创建序列化流对象
    9.          ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("employee.txt"));
    10.       // 写出对象
    11.       out.writeObject(e);
    12.       // 释放资源
    13.       out.close();
    14.       fileOut.close();
    15.       System.out.println("Serialized data is saved"); // 姓名,地址被序列化,年龄没有被序列化。
    16.       } catch(IOException i)   {
    17.            i.printStackTrace();
    18.       }
    19.   }
    20. }
    输出结果:
    Serialized data is saved

    3.3 ObjectInputStream类

    ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。

    构造方法

    • public ObjectInputStream(InputStream in): 创建一个指定InputStream的ObjectInputStream。

    反序列化操作1

    如果能找到一个对象的class文件,我们可以进行反序列化操作,调用ObjectInputStream读取对象的方法:

    • public final Object readObject () : 读取一个对象。

    1. public class DeserializeDemo {
    2.   public static void main(String [] args)   {
    3.        Employee e = null;
    4.        try {
    5.             // 创建反序列化流
    6.             FileInputStream fileIn = new FileInputStream("employee.txt");
    7.             ObjectInputStream in = new ObjectInputStream(fileIn);
    8.             // 读取一个对象
    9.             e = (Employee) in.readObject();
    10.             // 释放资源
    11.             in.close();
    12.             fileIn.close();
    13.       }catch(IOException i) {
    14.             // 捕获其他异常
    15.             i.printStackTrace();
    16.             return;
    17.       }catch(ClassNotFoundException c) {
    18.       // 捕获类找不到异常
    19.             System.out.println("Employee class not found");
    20.             c.printStackTrace();
    21.             return;
    22.       }
    23.        // 无异常,直接打印输出
    24.        System.out.println("Name: " + e.name); // zhangsan
    25.        System.out.println("Address: " + e.address); // beiqinglu
    26.        System.out.println("age: " + e.age); // 0
    27.   }
    28. }

    对于JVM可以反序列化对象,它必须是能够找到class文件的类。如果找不到该类的class文件,则抛出一个 ClassNotFoundException 异常。

    反序列化操作2

    另外,当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException异常。发生这个异常的原因如下:

    • 该类的序列版本号与从流中读取的类描述符的版本号不匹配

    • 该类包含未知数据类型

    • 该类没有可访问的无参数构造方法

    Serializable 接口给需要序列化的类,提供了一个序列版本号。serialVersionUID 该版本号的目的在于验证序列化的对象和对应类是否版本匹配。

    1. public class Employee implements java.io.Serializable {
    2.     // 加入序列版本号
    3.     private static final long serialVersionUID = 1L;
    4.     public String name;
    5.     public String address;
    6.     // 添加新的属性 ,重新编译, 可以反序列化,该属性赋为默认值.
    7.     public int eid;
    8.     public void addressCheck() {
    9.         System.out.println("Address check : " + name + " -- " + address);
    10.     }
    11. }

    3.4 练习:序列化集合

    1. 将存有多个自定义对象的集合序列化操作,保存到list.txt文件中。

    2. 反序列化list.txt ,并遍历集合,打印对象信息。

    案例分析

    1. 把若干学生对象 ,保存到集合中。

    2. 把集合序列化。

    3. 反序列化读取时,只需要读取一次,转换为集合类型。

    4. 遍历集合,可以打印所有的学生信息

    案例实现

    1. public class SerTest {
    2. public static void main(String[] args) throws Exception {
    3. // 创建 学生对象
    4. Student student = new Student("老王", "laow");
    5. Student student2 = new Student("老张", "laoz");
    6. Student student3 = new Student("老李", "laol");
    7. ArrayList arrayList = new ArrayList<>();
    8. arrayList.add(student);
    9. arrayList.add(student2);
    10. arrayList.add(student3);
    11. // 序列化操作
    12. // serializ(arrayList);
    13. // 反序列化  
    14. ObjectInputStream ois  = new ObjectInputStream(new FileInputStream("list.txt"));
    15. // 读取对象,强转为ArrayList类型
    16. ArrayList list  = (ArrayList)ois.readObject();
    17.     for (int i = 0; i < list.size(); i++ ){
    18.         Student s = list.get(i);
    19.       System.out.println(s.getName()+"--"+ s.getPwd());
    20.     }
    21. }
    22. private static void serializ(ArrayList arrayList) throws Exception {
    23. // 创建 序列化流
    24. ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("list.txt"));
    25. // 写出对象
    26. oos.writeObject(arrayList);
    27. // 释放资源
    28. oos.close();
    29. }
    30. }
  • 相关阅读:
    动态规划——123. 买卖股票的最佳时机 III
    旅游寄递自助化教程
    java中的Properties配置文件操作[61]
    【智能家居项目】FreeRTOS版本——多任务系统中使用DHT11 | 获取SNTP服务器时间 | 重新设计功能框架
    Java学习笔记3.9.1 Lambda表达式 - Lambda表达式入门
    前端培训丁鹿学堂:前端算法基础汇总
    学点设计模式,盘点Spring等源码中与设计模式的那些事之行为型模型
    【SpringCloud微服务】- Eureka服务注册与服务发现Discovery
    编辑器实现思路
    腾讯云轻量服务器WordPress建站宝塔一键部署
  • 原文地址:https://blog.csdn.net/qq_69748833/article/details/133283637