Java基础 第四节 第十八课

举报
我是小白呀iamarookie 发表于 2021/09/09 22:47:55 2021/09/09
【摘要】 序列化 概述ObjectOutputStream 类构造方法序列化操作 ObjectInputStream 类构造方法反序列化操作 (第一种)反序列化操作 (第二种) 练习: 序列化集合案...

概述

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

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

ObjectOutputStream 类

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

构造方法

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

构造举例, 代码如下:

public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("a.txt");
        ObjectOutputStream out = new ObjectOutputStream(fos);
    }

  
 
  • 1
  • 2
  • 3
  • 4

序列化操作

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

  • 该类必须实现java.io.Serializable接口, Serialzable 是一个标记接口. 不实现此接口的类将不会使任何状态序列化或反序列化, 会抛出 NotSerializableException
  • 该类的所有属性必须是可序列化的. 如果有一个属性不需要可序列化的, 则该属性必须注明是瞬态的, 使用 transient 关键字修饰
import java.io.Serializable;

public class Employee implements Serializable {
    public String name;
    public String address;
    public transient int age;  // transient 瞬态修饰成员, 不会被序列化

    public void addressCheck() {
        System.out.println("Address check: " + name + "--" + address);
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

写出对象方法

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

  
 
  • 1
  • 2
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Test89 {
    public static void main(String[] args) throws IOException {
        Employee e = new Employee();
        e.name = "我是小白";
        e.address = "西伯利亚";
        e.age = 20;
        try (
                FileOutputStream fos = new FileOutputStream("employee.txt");
                // 创建序列化流对象
                ObjectOutputStream out = new ObjectOutputStream(fos)
        ) {
            // 写出对象
            out.writeObject(e);
            out.close();
            // 姓名, 地址被序列化, 年龄没有被序列化
            System.out.println("Serialized data is saved");
        } catch (IOException exception) {
            exception.printStackTrace();
        }
    }
}

输出结果:
Serialized data is saved

  
 
  • 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

ObjectInputStream 类

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

构造方法

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

  
 
  • 1
  • 2

反序列化操作 (第一种)

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

  • public final Object readObject(): 读取一个对象
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class Test {
    public static void main(String[] args) throws IOException {
        Employee e = null;
        // 创建反序列化流
        try (
                FileInputStream fis = new FileInputStream("employee.txt");
                ObjectInputStream in = new ObjectInputStream(fis)
                ){
            // 读取一个对象
            e = (Employee) in.readObject();
            // 释放资源
            in.close();

        } catch (IOException | ClassNotFoundException i){
            i.printStackTrace();
        }

        // 无异常, 直接打印输出
        System.out.println("Name: " + e.name);
        System.out.println("Address: " + e.address);
        System.out.println("age: " + e.age);
    }
}

输出结果:
Name: 我是小白
Address: 西伯利亚
age: 0

  
 
  • 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

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

反序列化操作 (第二种)

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

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

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

import java.io.Serializable;

public class Employee implements Serializable {
    // 加入序列版本号
    private static final long serialVersionUID = 1L;
    public String name;
    public String address;
    // 添加新的属性, 重新编译. 可以反序列化, 该属性赋为默认值
    public int eid;

    public void addressCheck() {
        System.out.println("Address check: " + name + " -- " + address);
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

练习: 序列化集合

  1. 将存有多个自定义对象的集合序列化操作,

案例分析

  1. 把若干学生对象, 保存到集合中
  2. 把集合序列化
  3. 反序列化读取时, 只需要读取一次, 转换为结合类型
  4. 遍历集合, 可以打印所有的学生信息

案例实现

Student 类

import java.io.Serializable;

public class Student implements Serializable{
    private String name;
    private int age;

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

    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;
    }

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

  
 
  • 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

测试类

import java.io.*;
import java.util.ArrayList;

public class Test {
    public static void main(String[] args) throws Exception {
        // 创建学生对象
        Student s1 = new Student("小小白", 16);
        Student s2 = new Student("小白", 17);
        Student s3 = new Student("大白", 18);

        ArrayList<Student> arrayList = new ArrayList<>();
        arrayList.add(s1);
        arrayList.add(s2);
        arrayList.add(s3);

        // 序列化操作
        serialize(arrayList);

        // 反序列化
        ObjectInputStream ois  = new ObjectInputStream(new FileInputStream("list.txt"));
        // 读取对象, 强转为ArrayList类型
        ArrayList<Student> list  = (ArrayList<Student>)ois.readObject();
        System.out.println(list.toString());

    }
    private static void serialize(ArrayList<Student> arrayList) throws Exception {
        // 创建序列化流
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("list.txt"));
        // 写出对象
        out.writeObject(arrayList);
        // 释放资源
        out.close();
    }
}

  
 
  • 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

文章来源: iamarookie.blog.csdn.net,作者:我是小白呀,版权归原作者所有,如需转载,请联系作者。

原文链接:iamarookie.blog.csdn.net/article/details/111659946

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。