IO流(五)高级流——>序列化流和反序列化流

(3)序列化流和反序列流

序列化流和反序列流都属于字节流的部分
---1、序列化流/对象操作输出流
序列化流/对象操作输出流

可以把java中的对象写到本地文件中

构造方法

Public ObjectOutputStream(OutputStream out) 把基本流包装成高级流

成员方法

Public final void writeObject(Object obj) 把对象序列化(写出)到文件中去

序列化流的注意点

如果直接使用对象输出流将对象保存到文件时会出现 NotSerialzableException 异常,此时需要让javabean类实现Serializable接口。

注: Serializable接口里面是没有抽象方法的,称为: 标记型接口

一旦实现了这个接口,那么就表示当前的Student类可以被序列化

java 复制代码
package ObjectStream;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class ObjectStreamDemo1 {
    public static void main(String[] args) throws IOException {
        //1、创建对象
        Student stu = new Student("加强孙尚香",23);
        //2、创建序列化流的对象/对象操作输出流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("opp-IO\\a.txt"));
        //3、写出数据
        oos.writeObject(stu);

        oos.close();
    }
}
/*
   Serializable接口里面是没有抽象方法的,称为:标记型接口
    一旦实现了这个接口,那么就表示当前的Student类可以被序列化
 */
public class Student implements Serializable {
    private String name;
    private int age;

    public Student() {}
    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 +
                '}';
    }
}
---2、反序列化流/对象操作输入流
反序列化流/对象操作输入流

可以把序列化到本地文件中的对象,读取到程序中来

构造方法

public ObjectInputStream(InputStream out) 把基本流变成高级流

成员方法

public Object readObject() 把序列化到本地文件中的对象,读取到程序中来

java 复制代码
package ObjectStream;

import java.io.*;

public class ObjectStreamDemo1 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //1、创建对象
        Student stu = new Student("加强孙尚香",23);
        //2、创建序列化流的对象/对象操作输出流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("opp-IO\\a.txt"));
        //3、写出数据
        oos.writeObject(stu);
        oos.close();
        //1、创建反序列化流的对象/对象错综输入流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("opp-IO\\a.txt"));
        //2、读取数据
        Object o = ois.readObject();

        System.out.println(o);
        ois.close();
    }
}
---3、序列化流和反序列化流的细节

当把javabean信息保存到文件中,此时如果修改了javabean中的代码,则会重新计算出一个版本号,此时再次读取文件中对象信息时,会报错,版本号不匹配。所以如果我要修改javabean中的信息,就不可以了,怎么解决,看下面:

原因: 文件中的版本号,跟javabean的版本号不匹配

解决方案: 固定版本号。

即在javabean类中声明 private static final long serialVersionUID = 1L;

注: 这里版本名只能写成serialVersionUID

transient关键字

如果有一个成员变量的值我不想让它被序列化到本地文件,这时只需要在该成员变量前加上transient关键字。例如下面 private transient String address;

  • transient:瞬态关键字

  • 作用: 不会把当前属性序列化到本地文件当中

序列化流/反序列化流的细节汇总

(1)使用序列化流将对象写到文件时,需要让javabean类实现Seriallizable接口。否则,会出现NotSerializableException异常

(2) 序列化流写到文件中的数据是不能修改的,一旦修改就无法再次读回来了

(3)序列化对象后,修改了javabean类,再次反序列化,会不会有问题?

会出问题,会抛出 InvalidClassException 异常

解决方案: 给javabean类添加serialVersionUID( ++序列号、版本号++

(4)如果一个对象中的某个成员变量的值不想被序列化,有该如何实现呢?

解决方案: 给该成员变量加 transient 关键字修饰, ++该关键字标记的成员变量不参与序列化过程++

java 复制代码
package ObjectStream;

import java.io.Serial;
import java.io.Serializable;
/*
   Serializable接口里面是没有抽象方法的,称为:标记型接口
    一旦实现了这个接口,那么就表示当前的Student类可以被序列化
 */
public class Student implements Serializable {
    @Serial
    private static final long serialVersionUID = 2294754669754402810L; //固定版本号
    
    private String name;
    private int age;
    private transient String address;

    public Student() {}
    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 +
                '}';
    }
}
---4、综合练习(读写多个对象)

要读写多个对象,我们需要把所有对象保存到集合中,然后写到文件中,最后读取的时候,直接变量集合即可,防止不知道文件有多少对象出现读取完文件后还继续读时出现的异常

java 复制代码
package ObjectStream;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;

public class ObjectStreamDemo2 {
    public static void main(String[] args) throws IOException {
        Student s1 = new Student("zhangsan",23,"南京");
        Student s2 = new Student("lisi", 24, "重庆");
        Student s3 = new Student("wangwu",25,"北京");

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

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("opp-IO\\a.txt"));
        oos.writeObject(list);
        oos.close();
        
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("opp-IO\\a.txt"));

        ArrayList<Student> list2 = (ArrayList<Student>)ois.readObject();

        for (Student s: list2) {
            System.out.println(s);
        }
        ois.close();

    }
}
package ObjectStream;

import java.io.Serial;
import java.io.Serializable;
/*
   Serializable接口里面是没有抽象方法的,称为:标记型接口
    一旦实现了这个接口,那么就表示当前的Student类可以被序列化
 */
public class Student implements Serializable {
    @Serial
    private static final long serialVersionUID = 4043649055566315038L;
    //固定版本号
    private String name;
    private int age;
    private transient String address;

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

    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 +
                ", address='" + address + '\'' +
                '}';
    }
}