概述
对象序列化:就是将对象写入到磁盘中,或者在网络中传输对象。
这种机制就是使用一个字节序列,这个序列包含对象的信息:对象类型,对象数据,对象中存储的属性等等。字节序列写入到文件当中,相当于在文件中长久的保存一个对象的信息。
反序列化当然就是将对象的信息从文件中取出来,重构对象。
对象序列化流和反序列化流分别是:
- ObjectOutputStream(对象序列化流)
- ObjectInputStream(对象反序列化流)
对象序列化流ObjectOutputStream
序列化流继承自抽象类输出流
ObjectOutputStream将Java对象的原始数据类型和图形写入OutputStream。 可以使用ObjectInputStream读取(重构)对象。 可以通过使用流的文件来实现对象的持久存储。 如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象。
- 构造方法
public ObjectOutputStream(OutputStream out)
创建一个写入指定的OutputStream的ObjectOutputStream。 该构造函数将序列化流头写入底层流; 调用者可能希望立即刷新流,以确保在读取头部时接收ObjectInputStreams的构造函数不会阻塞。
参数类型是OutputStream,所以我们可以用FileOutputStream之类进行传参。
异常
IOException - 如果在写入流标题时发生I / O错误
SecurityException - 如果不可信子类非法覆盖安全敏感方法
NullPointerException - 如果 out是 null
- 写对象的方法:
public final void writeObject(Object obj)
将指定的对象写入ObjectOutputStream。 写入对象的类,类的签名以及类的非瞬态和非静态字段的值以及其所有超类型。 可以使用writeObject和readObject方法覆盖类的默认序列化。 由该对象引用的对象被传递性地写入,以便可以通过ObjectInputStream重构对象的完整等价图。
- 使用举例1(String类写入)
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("D://filetest//writed.txt"));
String i=new String("wwww");
oos.writeObject(i);
oos.close();
使用举例2(自定义类写入)
import java.io.Serializable;
public class Student implements Serializable {
String ID;
String name;
public Student() {
}
public Student(String ID, String name) {
this.ID = ID;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"ID='" + ID + '\'' +
", name='" + name + '\'' +
'}';
}
}
import java.io.*;
public class IoDemo {
public static void main(String[] args) throws IOException {
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("D://filetest//writed.txt"));
Student stu=new Student("12","王民");
oos.writeObject(stu);
oos.close();
}
}
public class Student implements Serializable:这里最重要的是如果是你要序列化的类,那个类要implements Serializable接口否则会抛异常。这个结构没有方法,只需要实现就行。
- Serializable 接口
public interface Serializable类的序列化由实现java.io.Serializable接口的类启用。 不实现此接口的类将不会使任何状态序列化或反序列化。 可序列化类的所有子类型都是可序列化的。 序列化接口没有方法或字段,仅用于标识可串行化的语义。
反序列化流ObjectInputStream
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("D://filetest//writed.txt"));
Object obj = ois.readObject();
Student stu1=(Student)obj;//向下转型
System.out.println(stu1.toString());
ois.close();
InvalidClassException
这块的标题是一个异常,当然要说一下什么时候遇到这个异常。
public class InvalidClassException extends ObjectStreamException当序列化运行时检测到类中的以下问题之一时抛出:
- 类的串行版本与从流中读取的类描述符的类型不匹配 (与当初write的类不同,此时类可能被修改)
- 该类包含未知的数据类型
- 该类没有可访问的无参数构造函数
基于上面的问题,只要write之后类被修改了,那么就会抛出这个异常无法读取。
于是我们有了下面两个问题:
- 1:如果出现问题该如何让解决?(例如:类被修改了此时还想读)
这个问题我们先看我们的异常样子
Exception in thread “main” java.io.InvalidClassException: Student;local class incompatible:
stream classdesc serialVersionUID = 5583702560695495207,
local class serialVersionUID = -6022075613783040060
这里我们看到问题时我们修改类前后类的序列化UID不同
我们在来看Serializable接口文档里面直接给出我们的解决方案:
如果可序列化类没有显式声明serialVersionUID,则序列化运行时将根据Java(TM)对象序列化规范中所述的类的各个方面计算该类的默认serialVersionUID值。 但是, 强烈建议所有可序列化的类都明确声明serialVersionUID值,因为默认的serialVersionUID计算对类详细信息非常敏感,这可能会因编译器实现而异,因此可能会在反InvalidClassException化期间导致InvalidClassException的InvalidClassException。 因此,为了保证不同Java编译器实现之间的一致的serialVersionUID值,一个可序列化的类必须声明一个显式的serialVersionUID值。 还强烈建议,显式的serialVersionUID声明在可能的情况下使用private修饰符,因为这种声明仅适用于立即声明的类 - serialVersionUID字段作为继承成员无效。 数组类不能声明一个显式的serialVersionUID,所以它们总是具有默认的计算值,但是对于数组类,放弃了匹配serialVersionUID值的要求。
哈,这里解决方案就出来了,声明一个显式的serialVersionUID值,使用private修饰符。
- 2:如果类中某个成员变量不想被序列化怎么做?(例如:Student类里面的ID不想被序列化)
使用transient修饰变量 例如:transient String ID;
import java.io.Serializable;
public class Student implements Serializable {
transient String ID;
String name;
private static final long serialVersionUID=42L;
public Student() {
}
public Student(String ID, String name) {
this.ID = ID;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"ID='" + ID + '\'' +
", name='" + name + '\'' +
'}';
}
}
import java.io.*;
public class IoDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("D://filetest//writed.txt"));
Student stu=new Student("12","王民");
oos.writeObject(stu);
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("D://filetest//writed.txt"));
Object obj = ois.readObject();
Student stu1=(Student)obj;//向下转型
System.out.println(stu1.toString());
ois.close();
}
}