序列化/反序列化
- Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该
对象的数据、对象的类型和对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。
- 反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。
对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。
- 把对象以流的方式,写入到文件中保存,叫写对象,也叫对象的序列化
ObjectOutputStream:对象的序列化流
- 把文件中保存的对象,以流的方式读取出来,叫读对象,也叫对象的反序列化
ObjectInputStream:对象的反序列化
- 序列化和反序列化的时候,会抛出
NotSerializableException 没有序列化异常:
- 类通过实现
java.io.Serializable 接口来启动其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化
- Serializable接口也叫标记型接口,会给类添加一个标记,序列化或反序列化时会检测类上是否有这个标记
- 反序列化的前提:
- 类必须实现Serializable
- 必须存在类对应的class文件
ObjectOutputStream类:对象的序列化流
- ObjectOutputStream extends OutputStream :将Java对象的原始数据类型写出到文件,实现对象的持久存储。
构造方法
public ObjectOutputStream(OutputStream out) : 创建一个指定OutputStream的ObjectOutputStream。
特有的成员方法
void writeObject(Object obj) : 将指定的对象写入 ObjectOutputStream。
public class Person implements Serializable {
private String name;
private int age;
...
}
public class Demo {
public static void main(String[] args) throws IOException {
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("D:\\document\\code\\xuexi\\java\\aaa\\a.txt"));
oos.writeObject(new Person("王二",90));
oos.close();
}
}
- ObjectInputStream extends InputStream:将之前使用ObjectOutputStream序列化的原始数据恢复为对象。
构造方法
public ObjectInputStream(InputStream in) : 创建一个指定InputStream的ObjectInputStream。
特有的成员方法
Object readObject() :从 ObjectInputStream 读取对象。
- readObject方法声明抛出了
ClassNotFoundException(class文件找不到异常)
public class Demo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("D:\\document\\code\\xuexi\\java\\aaa\\a.txt"));
Object o = ois.readObject();
ois.close();
System.out.println(o);//Person{name='王二', age=90}
}
}
反序列化操作2
- 当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个
InvalidClassException异常。
- 发生这个异常的原因如下:
- 该类的序列版本号与从流中读取的类描述符的版本号不匹配 :
Serializable 接口给需要序列化的类,提供了一个序列版本号。serialVersionUID 该版本号的目的在于验证序列化的对象和对应类是否版本匹配。
- 该类包含未知数据类型
- 该类没有可访问的无参数构造方法
- 每次修改类的定义,都会给class文件生成一个新的序列号
- 解决方案:无论是否对类的定义进行修改,都不重新生成新的序列号,可以手动添加一个序列号
- 格式在Serializable接口规定:可序列化类可以通过声明名为 "serialVersionUID" 的字段(该字段必须是静态 (static)、最终 (final) 的 long 型字段)显式声明其自己的 serialVersionUID:
static final long serialVersionUID = 42L;常量,不能改变
public class Employee implements java.io.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);
}
}
transient关键字:瞬态关键字
- 被transient修饰的成员变量,不能被序列化
- 静态变量也不能被序列化