使用 Externalizable 进行反序列化的时候,会调用被序列化类的无参构造方法去创建一个新的对象,然后再将被保存对象的字段值复制过去。否则的话,会抛出以下异常:
java.io.InvalidClassException: com.cmower.java_demo.xuliehua1.Wanger; no valid constructorat java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:150)
at java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:790)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1782)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
at com.cmower.java_demo.xuliehua1.Test.main(Test.java:27)
2)新增了两个方法 writeExternal() 和 readExternal(),实现 Externalizable 接口所必须的。
然后,我们再在测试类中打印序列化前和反序列化后的对象。
// 初始化Wanger wanger = new Wanger();
wanger.setName("王二");
wanger.setAge(18);
System.out.println(wanger);
// 把对象写到文件中
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("chenmo"));) {
oos.writeObject(wanger);
} catch (IOException e) {
e.printStackTrace();
}
// 从文件中读出对象
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("chenmo")));) {
Wanger wanger1 = (Wanger) ois.readObject();
System.out.println(wanger1);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
// Wanger{name=王二,age=18}
// Wanger{name=null,age=0}
从输出的结果看,反序列化后得到的对象字段都变成了默认值,也就是说,序列化之前的对象状态没有被“冻结”下来。
为什么呢?因为我们没有为 Wanger 类重写具体的 writeExternal() 和 readExternal() 方法。那该怎么重写呢?
@Overridepublic void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(name);
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = (String) in.readObject();
age = in.readInt();
}
1)调用 ObjectOutput 的 writeObject() 方法将字符串类型的 name 写入到输出流中;
2)调用 ObjectOutput 的 writeInt() 方法将整型的 age 写入到输出流中;
3)调用 ObjectInput 的 readObject() 方法将字符串类型的 name 读入到输入流中;
4)调用 ObjectInput 的 readInt() 方法将字符串类型的 age 读入到输入流中;
再运行一次测试了类,你会发现对象可以正常地序列化和反序列化了。
序列化前:Wanger{name=王二,age=18}
序列化后:Wanger{name=王二,age=18}
让我先问问你吧,你知道 private static final long serialVersionUID = -2095916884810199532L; 这段代码的作用吗?
嗯……
serialVersionUID 被称为序列化 ID,它是决定 Java 对象能否反序列化成功的重要因子。在反序列化时,Java 虚拟机会把字节流中的 serialVersionUID 与被序列化类中的 serialVersionUID 进行比较,如果相同则可以进行反序列化,否则就会抛出序列化版本不一致的异常。
当一个类实现了 Serializable 接口后,IDE 就会提醒该类最好产生一个序列化 ID,就像下面这样:
1)添加一个默认版本的序列化 ID:
private static final long serialVersionUID = 1L。