序列化流

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

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

IO流(03)--序列化流、打印流-LMLPHP

ObjectOutputStream类

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

构造方法
  • public ObjectOutputStream(OutputStream out):创建一个指定的OutputStream的ObjectOutputStream类对象
特有的独有方法:
  • void writeObject(Object obj):将指定的对象写入到ObjectOutputStream类对象中。
序列化操作
  1. ​一个对象想要能够序列化和反序列化,必须满足两个条件:

    • 该类必须实现java.io.Serializable接口,Serializable接口,是一个标记型接口,如果该类没有实现Serializable接口,将会抛出NotSerializableException。

    • 该类的所有属性必须是可以实现序列化或者反序列化。如果有一个属性不想让它参与序列化,则该属性必须标明是瞬态的,瞬时的,这个关键字是transient

public class Student implements Serializable {

    private String name;
    private transient Integer age;// 不让age属性参与序列化

}

ObjectInputStream类

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

构造方法
  • ​public ObjectInputStream(InputStream in):创建一个指定的InputStream的对象反序列化流对象。
特有的方法:
  • ​public final Object readObject():从反序列化流中读取一个对象。

对于JVM来说,能够进行反序列的对象 ,前提条件是必须能够找到class文件的类,如果找不到该类的class文件,则会抛出一个ClassNotFoundException异常。

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

  • 该类的序列化版本号与从流中读取出来描述该类的版本号不一致。

  • 该类包含了未知数据类型。

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

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

代码演示:

// 序列化操作类
public class Demo01ObjectOutputStream {
    public static void main(String[] args) throws IOException {
        //1.创建ObjectOutputStream流对象,构造方法中传递指定的字节输出流。
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day30_IO\\student.txt"));
        //2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中。
        //2.1 先创建一个对象
        Student s = new Student("小孙", 30);
        s.score = 60;
        //2.2写对象到文件中
        oos.writeObject(s);
        //3.释放资源。
        oos.close();
    }
}
// 反序列化类操作
public class Demo02ObjectInputStream {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 1. 创建一个ObjectInputStream流对象,构造方法中传递一个字节输入流对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day30_IO\\student.txt"));
        // 2. 使用ObjectInputStream流对象中的方法readObject,读取保存在文件中的对象数据
        Object obj = ois.readObject();
        // 3.释放资源。
        ois.close();
        // 4. 查看对象的数据
        System.out.println(obj);// Student{name='小孙', age=30}
        if ( obj instanceof Student) {
            Student student = (Student)obj;
            System.out.println(student.getAge() + "--" + student.getName());
        } else {
            System.out.println("转换失败");
        }
    }
}
// 需要被序列化的类
import java.io.Serializable;
public class Student implements Serializable {
    // 可以选择手动自定义一个序列化版本号
    private static final long serialVersionUID = 1L;
    //private static String name;
    private String name;
    private Integer age;
    private transient String address = "郑州市";
    transient int score;// 0
}

原理分析:

IO流(03)--序列化流、打印流-LMLPHP

练习:存储一堆对象,实现序列化和反序列化动作。
import java.io.Serializable;

public class Student implements Serializable {
    // 可以选择手动自定义一个序列化版本号
    private static final long serialVersionUID = 1L;
    //private static String name;
    private String name;
    private Integer age;
    private transient String address = "郑州市";
    transient int score;// 0
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public Student() {
    }
    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
        address = "郑州市";
    }
   @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                ", score=" + score +
                '}';
    }
}


@SuppressWarnings("unchecked")
public class DemoTestSerialize {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //1.定义多个对象,存储在集合中
        ArrayList<Student> list = new ArrayList<>();
        list.add(new Student("小孙",30));
        list.add(new Student("小王",20));
        list.add(new Student("小赵",40));
        list.add(new Student("小刘",10));
        list.add(new Student("小丽",25));
        //2.使用序列化技术,把该集合对象写入到文件中
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day30_IO\\student.txt"));
        //3.写入进去,调用writeObject
        oos.writeObject(list);
        //4.使用反序列化技术,把文件中保存的集合对象读取出来
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day3_IO\\student.txt"));
        //5.读取对象数据,调用readObject()方法
        Object obj = ois.readObject();
        //6.向下转型
        if (obj instanceof ArrayList){
            ArrayList<Student> students = (ArrayList<Student>)obj;
            //7.遍历该集合对象
            for (Object student:students){
                System.out.println(student);
            }
        }
    }
}

打印流

java.io.PrintStream类能够很方便打印各种数据类型的值。

构造方法
  • public PrintStream(String filename):使用指定的文件名创建一个新的打印流对象。
改变打印流的方向

正常System.out就是PrintStream类型的,数据的流动的位置在控制台中。改变数据的流动位置。通过System.setOut(PrintStream print)来改变流向。

 PrintStream out = System.out;
 out.print(123);// 在控制台中
 // 构造方法创建一个打印流对象
 PrintStream printStream = new PrintStream("day30_IO\\print.txt");
 // 改变打印流的方向为"day30_IO\\print.txt"路径
 System.setOut(printStream);
 System.out.println("我已经改变了输出数据的位置");
 System.out.println("我想在控制台中输出数据");
 System.out.println("啦啦啦啦啦");
12-18 10:05