1.2 序列化过程中特殊处理方法

从零开始读 Java 源码 第一章 接口篇 Serializable(2)_c#

类在序列化反序列化过程中需要特殊的方法进行处理:

从零开始读 Java 源码 第一章 接口篇 Serializable(2)_.net_02

1.2.1 writeObject

从零开始读 Java 源码 第一章 接口篇 Serializable(2)_c#_03

        writeObject 方法是对当前对象的部分类进行写操作,所以在 readObject 方法中可以读取它。默认机制使用 out.defaultWriteObject 保存对象字段 。 这个方法不需要关心它的状态属于父类型或者子类型。状态是保存在 ObjectOutputStream 写入时私有属性,或者使用 DataOutput 支持的私有数据段。

        进行 IO 写入的过程,可以用 writeObject() ,自己对写入字段顺序、内容进行控制

1.2.2 readObject

从零开始读 Java 源码 第一章 接口篇 Serializable(2)_.net_04

        readObject 方法 用于读取对象响应从流并装载对象属性。它可能会回调 in.defaultReadObject 来触发默认的机制来保存对象中未被 static transient 修饰的属性。这个 defaultReadObject 方法用到信息在流中,用于设置保存当前类中命名的属性。这个处理在添加新的属性的过程中就已经逐步完成。这个方法不需要关心这个状态值属于父类还是子类,状态值再 ObjectOutStream 使用 writeObject 方法的过程中已经写入到它的私有属性中了,或者使用实现了DataOut 的方法来初始化对应的类型。

整理一下关键信息:

  1. 使用static 或者transient 修饰的属性不会被序列化。
  2. readObject用于从流中读取对象。

尝试自定义三个方法:

import java.io.*;

public class B extends A implements Serializable {
private int c;
private transient int d = 100;
private static int e = 50;
public B(int c){
super(0);
this.c = c;
}

public int getD() {
return d;
}

public void setD(int d) {
this.d = d;
}

public static int getE() {
return e;
}

public static void setE(int e) {
B.e = e;
}

@Override
public String toString() {
return "{b="+b+",c="+c+",d="+d+",e="+e+"}";
}

private void writeObject(ObjectOutputStream oos)
throws IOException {
oos.defaultWriteObject();
oos.writeInt(b);
oos.writeInt(c);
}

private void readObject(ObjectInputStream ois)
throws ClassNotFoundException, IOException {
ois.defaultReadObject();
this.b = ois.readInt();
this.c = ois.readInt();
B.e = 50;
this.d = 100;
}
private void readObjectNoData()
throws ObjectStreamException {
this.d = 100;
this.e = 50;
}
}

从零开始读 Java 源码 第一章 接口篇 Serializable(2)_c#_05

发现可正常存取对象,设置 triansent/static 变量的值。

1.2.3 readObjectNoData

从零开始读 Java 源码 第一章 接口篇 Serializable(2)_linq_06

        readObjectNoData 这个方法是负责对特定的序列化的类没有被列为反序列化超类的类进行初始化的,序列化过程中没有列出这个类需要进行反序列化。然后接收的时候,发现需要对对应类进行初始化,这个时候接收者的父类版本没有继承发送者版本。它也可能发生在序列化流中断;因此,readObjectNoData 对于初始化反序列化,即使出现不利于进行反序列化数据流。

它是什么意思呢?

  1. 发送方序列化的子类没有继承序列化的父类,然后进行了序列化操作;
public class B  implements Serializable {
private static final long serialVersionUID = 2L;
protected int b;
private int c;
private transient int d = 100;
private static int e = 50;
public B(int c){
// super(0);
this.c = c;
}

public int getD() {
return d;
}

public void setD(int d) {
this.d = d;
}

public static int getE() {
return e;
}

public static void setE(int e) {
B.e = e;
}

@Override
public String toString() {
return super.toString()+"{b="+b+",c="+c+",d="+d+",e="+e+"}";
}

private void writeObject(ObjectOutputStream oos)
throws IOException {
oos.defaultWriteObject();
oos.writeInt(c);
}

private void readObject(ObjectInputStream ois)
throws ClassNotFoundException, IOException {
ois.defaultReadObject();
this.c = ois.readInt();
B.e = 50;
this.d = 100;
}
}

import com.dyy.jdk8.test.B;

import java.io.*;
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Main solution = new Main();

B a = new B(4);
// a.setB(7);
a.setD(8);
B.setE(9);
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("E:/aa.txt")));
oo.writeObject(a);
B.setE(0);
// //反序列化
// ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("E:/aa.txt")));
// B person = (B) ois.readObject();
// System.out.println(person);
}
}

        2. 接收方子类继承了序列化的父类,两边父类的版本不一致。

        3. 需要给父类对象一个初始值,于是,readObjectNoData 就派上用场。

import java.io.*;

public class B extends A implements Serializable {
private static final long serialVersionUID = 2L;
protected int b;
private int c;
private transient int d = 100;
private static int e = 50;
public B(int c){
// super(0);
this.c = c;
}

public int getD() {
return d;
}

public void setD(int d) {
this.d = d;
}

public static int getE() {
return e;
}

public static void setE(int e) {
B.e = e;
}

@Override
public String toString() {
return super.toString()+"{b="+b+",c="+c+",d="+d+",e="+e+"}";
}

private void writeObject(ObjectOutputStream oos)
throws IOException {
oos.defaultWriteObject();
oos.writeInt(c);
}

private void readObject(ObjectInputStream ois)
throws ClassNotFoundException, IOException {
ois.defaultReadObject();
this.c = ois.readInt();
B.e = 50;
this.d = 100;
}
}
public class A implements Serializable{
private static final long serialVersionUID = 1L;
protected int b;

int t;

public int getT() {
return t;
}

public void setT(int t) {
this.t = t;
}

//显示下面这句就不会报错
public A(){}

public A(int b){
this.b = b;
}
public int getB() {
return b;
}

public void setB(int b) {
this.b = b;
}

@Override
public String toString() {
return "{t="+t+"}";
}

private void readObjectNoData()
throws ObjectStreamException {
this.t=10;
}
}

import com.dyy.jdk8.test.B;

import java.io.*;
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Main solution = new Main();

// B a = new B(4);
a.setB(7);
// a.setD(8);
// B.setE(9);
// ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("E:/aa.txt")));
// oo.writeObject(a);
// B.setE(0);
// //反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("E:/aa.txt")));
B person = (B) ois.readObject();
System.out.println(person);
}
}

从零开始读 Java 源码 第一章 接口篇 Serializable(2)_.net_07

所以就是用来写默认值的。

1.2.4 writeReplace

从零开始读 Java 源码 第一章 接口篇 Serializable(2)_.net_08

从零开始读 Java 源码 第一章 接口篇 Serializable(2)_linq_09

指定一个可供替代的对象可供使用,当写一个对象到流中的时候,需要实现这个特定的方法伴随着明确的签名:

任意访问修饰符 Object writeReplace() throws ObjectStreamException;

        writeReplace 方法是被序列化接口调用的,如果这个方法存在,它将从方法中访问一个定义在类中已被序列化的对象。然后这个方法可以是私有、受保护、包权限。子类访问这个方法是根据访问权限来的。

        当需要从流中读取对象来进行初始化时,可使用这个额外签名来进行指定替换。

Object writeReplace() throws ObjectStreamException{

return new B(100);

}

可以看出,对象被 writeReplace 方法写入的对象替代。可看作是代码层面的写屏障。

从零开始读 Java 源码 第一章 接口篇 Serializable(2)_linq_10

 

1.2.4 readResolve

从零开始读 Java 源码 第一章 接口篇 Serializable(2)_c#_11

任意访问修饰符 Object readResolve() throws ObjectStreamException;

readResolve 方法 和 writeReplace 方法遵循同样的调用规则。可看作是代码层面的读屏障。

import java.io.*;

public class B extends A implements Serializable {
private static final long serialVersionUID = 2L;
protected int b;
private int c;
private transient int d = 100;
private static int e = 50;

public B(int c) {
// super(0);
this.c = c;
}

public int getD() {
return d;
}

public void setD(int d) {
this.d = d;
}

public static int getE() {
return e;
}

public static void setE(int e) {
B.e = e;
}

@Override
public String toString() {
return super.toString() + "{b=" + b + ",c=" + c + ",d=" + d + ",e=" + e + "}";
}

private void writeObject(ObjectOutputStream oos)
throws IOException {
oos.defaultWriteObject();
oos.writeInt(c);
}

private void readObject(ObjectInputStream ois)
throws ClassNotFoundException, IOException {
ois.defaultReadObject();
this.c = ois.readInt();
B.e = 50;
this.d = 100;
}

Object readResolve() throws ObjectStreamException{
return new B(100);
}
}

从零开始读 Java 源码 第一章 接口篇 Serializable(2)_序列化_12

        也能达到和 writeReplace 相似的效果。