这个连接包含了常用的流------IO流(总篇章)  



对象序列化​:就是将对象保存到磁盘中,或者在网络中传输对象

这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息

字节序列写到文件后,相当于文件中持久保存了一个对昂的信息

反之。该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化



要实现序列化与反序列化,就要使用对象序列化流和对象反序列化流:


  • 对象序列化流:ObjectOutputStream
  • 对象反序列化流:ObjectInputStream



对象序列化流:ObjectOutputStream


  • 将Java对象的原始数据类型和图像写入OutputStream。可以使用ObjectInputStream读取(重构)对象。可以通过使用流的文件来实现对象的持久存储。如果流是网络套接字流,则可以再另外一个主机上或另一个进程中重构对象
  • 构造方法​:ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream
  • 序列化对象的方法:void writeObject(Object obj):将指定的对象写入ObjectOutputStream



下面我们放代码演示



学生类


package com.testIO;

/**
* @author 林高禄
* @create 2020-05-12-21:39
*/
public class Student {
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;
}
}


序列化Demo


package com.testIO;

import java.io.*;

/**
* @author 林高禄
* @create 2020-05-10-13:11
*/
public class ObjectOutputStreamDemo {
public static void main(String[] args) throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test//src//com//testIO//oos.txt"));
Student s = new Student("林高禄",27);
oos.writeObject(s);
oos.close();
}
}


运行出现以下错误:

Exception in thread "main" java.io.NotSerializableException: com.testIO.Student
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
    at com.testIO.ObjectOutputStreamDemo.main(ObjectOutputStreamDemo.java:13)



NotSerializableException

抛出一个实例需要​Serializable​接口。序列化运行时或实例的类可能会抛出此异常



Serializable

类的序列化由实现​java.io.Serializable​接口的类启用。不实现此接口的类将不会使任何状态序列化或反序列化。可序列化类的所有子类型都是可序列化的。序列化接口没有方法或字段,仅用于标识可串行化的语义。



到这里我们就明白报 ​java.io.NotSerializableException: com.testIO.Student​异常的原因是,学生类​Student​没有实现​Serializable​接口,下面我们把​Student​代码修改一下,去实现​Serializable​接口。


package com.testIO;

import java.io.Serializable;

/**
* @author 林高禄
* @create 2020-05-12-21:39
*/
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;
}
}


再次运行,得到​oos.txt​文件含有一下内容


对象序列化流与对象反序列化流(ObjectOutputStream,ObjectInputStream)_对象反序列化流


 到这里,说明我们的序列化成功

注意:


  • 一个对象要想被序列化,该对象所属的类必须实现​Serializable​接口。
  • Serializable​是一个​标记接口​,实现该接口,不需要重写任何方法。



对象反序列化流:ObjectInputStream


  • ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象
  • 构造方法​:ObjectInputStream(InputStream in):创建从指定的InputStream读取的ObjectInputStream
  • 反序列化对象的方法​:Object readObject():从ObjectInputStream读取一个对象


package com.testIO;

import java.io.*;

/**
* @author 林高禄
* @create 2020-05-10-13:11
*/
public class ObjectInputStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test//src//com//testIO//oos.txt"));
Object obj = ois.readObject();
Student s = (Student)obj;
System.out.println(s.getName()+","+s.getAge());
ois.close();
}
}


输出:

林高禄,27



到这里,说明我们的反序列化成功



下面我们做一下骚修改,反序列化之前,我们对Student类做了些修改,比如重写了toString()方法


package com.testIO;

import java.io.Serializable;

/**
* @author 林高禄
* @create 2020-05-12-21:39
*/
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 +
'}';
}
}


再执行一次反序列化Demo,控制台报以下异常

Exception in thread "main" java.io.InvalidClassException: com.testIO.Student; local class incompatible: stream classdesc serialVersionUID = 5613672065288821876, local class serialVersionUID = 7003572463648716337
    at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:621)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
    at com.testIO.ObjectInputStreamDemo.main(ObjectInputStreamDemo.java:12)



InvalidClassException:

当序列化运行时检测到类中的以下问题之一时抛出


  • 类的串行版本与从流中读取的类描述符的类型不匹配
  • 该类包含未知的数据类型
  • 该类没有可访问的无惨构造函数



stream classdesc serialVersionUID = 5613672065288821876, local class serialVersionUID = 7003572463648716337

从这里我们知道,我们序列化是学生类的serialVersionUID = 5613672065288821876,而修改类后,local class serialVersionUID = 7003572463648716337,所以反序列异常



serialVersionUID

序列化运行时与每个可序列化的类关联一个版本号,称为​serialVersionUID​,它在反序列化的过程中使用,以验证序列化对象的发送者和接收者是否加载了与序列化兼容的对象的类。如果接收者已经具有与对应发件人类别不用的​serialVersionUID​的对象加载了一个类,则反序列化将导致一个​InvalidClassException​。一个可序列化的类可以通过声明一个名为“​serialVersionUID​”的字段来显式地声明它自己的​serialVersionUID​,该字段必须是​static​,​final​,和​long​类型。



知道原因之后,我们又修改了学生类,声明​serialVersionUID


package com.testIO;

import java.io.Serializable;

/**
* @author 林高禄
* @create 2020-05-12-21:39
*/
public class Student implements Serializable {
private static final long serialVersionUID = 22L;
private String name;
private int age;
private transient int bb;

public Student() {
}

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

public static Long getSerialVersionUID() {
return serialVersionUID;
}

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

public int getBb() {
return bb;
}

public void setBb(int bb) {
this.bb = bb;
}
}
package com.testIO;

import java.io.*;

/**
* @author 林高禄
* @create 2020-05-10-13:11
*/
public class ObjectOutputStreamDemo {
public static void main(String[] args) throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test//src//com//testIO//oos.txt"));
Student s = new Student("林高禄",27,55);
oos.writeObject(s);
oos.close();
}
}



执行序列化Demo之后,在修改Student类的代码,比如重写toString()方法,再执行反序列化Demo



package com.testIO;

import java.io.Serializable;

/**
* @author 林高禄
* @create 2020-05-12-21:39
*/
public class Student implements Serializable {
private static final long serialVersionUID = 22L;
private String name;
private int age;
private transient int bb;

public Student() {
}

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

public static Long getSerialVersionUID() {
return serialVersionUID;
}

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

public int getBb() {
return bb;
}

public void setBb(int bb) {
this.bb = bb;
}

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

import java.io.*;

/**
* @author 林高禄
* @create 2020-05-10-13:11
*/
public class ObjectInputStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test//src//com//testIO//oos.txt"));
Object obj = ois.readObject();
Student s = (Student)obj;
System.out.println(s.getName()+","+s.getAge()+","+s.getBb());
ois.close();
}
}


输出:

林高禄,27,0



反序列化成功,但是我们发现bb=0,这是为什么呢,注意看学生类属性​bb​的定义,​ private transient int bb​,关键字​transient ​作用就是使得修饰的属性不被序列化,所以反序列得出来的值是默认值0。