类实现了序列化接口,但是存在没有实现序列化接口的成员,运行报错:java.io.NotSerializableException
需要使用 transient 关键字修饰没有实现序列化接口的成员。
值得注意的是,如果没有实现序列化接口的成员变量的值是 null,那么即便不加 transient 关键字也不会报错。
静态变量为什么无法序列化?
静态变量不参与序列化,序列化的是对象的成员字段。
多引用写入问题:同一个引用,多次写入不同的对象内容,但取出的对象是一模一样的 演示代码:
// 序列化
FileOutputStream fos = new FileOutputStream(filePath);
ObjectOutputStream oos = new ObjectOutputStream(fos);
Person2 personWrite = new Person2("wzc", 32);
oos.writeObject(personWrite);
personWrite.setAge(33);
oos.writeObject(personWrite);
oos.close();
// 反序列化
FileInputStream fis = new FileInputStream(filePath);
ObjectInputStream ois = new ObjectInputStream(fis);
Person2 personRead1 = (Person2) ois.readObject();
Person2 personRead2 = (Person2) ois.readObject();
ois.close();
System.out.println("personWrite:" + personWrite);
System.out.println("personRead1:" + personRead1);
System.out.println("personRead2:" + personRead2);
System.out.println("personRead1 == personRead2:" + (personRead1 == personRead2));
打印信息:
personWrite:Person2@692404036{name='wzc', age=33}
personRead1:Person2@1072408673{name='wzc', age=32}
personRead2:Person2@1072408673{name='wzc', age=32}
personRead1 == personRead2:true
第一次使用 personWrite 写入的对象内容是 “wzc”, 32;
第二次使用 personWrite 写入的对象内容是“wzc”, 33;
但是,反序列化读取到的是一模一样的对象。
解决办法:
- 在第二次写入之前增加代码 oos.reset();
- 把第二次写入的代码:oos.writeObject(personWrite); 替换为 oos.writeUnshared(personWrite);
- 尽量避免多引用写入,使用不同的引用。
父类实现了Serializable,子类没有, 子类是否可以进行序列化?
可以。
子类实现序列化,父类不实现序列化,如何序列化父类的数据?
首先,要给父类添加空参构造器,否则会报错:java.io.InvalidClassException: com.java.advanced.features.io.serialize.serializable.Man; no valid constructor;
其次,让子类负责序列化(反序列化)父类的域。
代码如下:
public class Person7 {
public String name;
public int age;
// 添加了无参构造器
public Person7() {
}
public Person7(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Man3 extends Person7 implements Serializable {
public double salary;
public Man3(String name, int age, double salary) {
super(name, age);
this.salary = salary;
}
private void writeObject(ObjectOutputStream oos) throws IOException {
// 先序列化本类对象
oos.defaultWriteObject();
// 再序列化父类的域
oos.writeObject(name);
oos.writeInt(age);
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
// 先反序列化本类对象
ois.defaultReadObject();;
// 再反序列化父类的域
name = (String) ois.readObject();
age = ois.readInt();
}
}
序列化的时候多一个字段,反序列化的时候少一个字段,或者序列化的时候少一个字段,反序列化的时候多一个字段,会不会报错?
需要显式地声明 serialVersionUID 的值,如为 1L。
private static final long serialVersionUID = 1L;
因为计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的 InvalidClassException。
如果类的 serialVersionUID 是一致的,即便在序列化时的类和反序列化时的类有些不同,该对象仍会被尽最大限度完成反序列化。
writeReplace,writeObject, readResolve,readObject 的执行顺序
writeReplace 先于writeObject, readResolve后于readObject
反序列化打破单例,如何解决?
给单例添加 readResovle() 方法:
public class SingletonSerializeFix implements Serializable {
private static final long serialVersionUID = 1L;
private SingletonSerializeFix() {
//no instance
}
public static SingletonSerializeFix getInstance() {
return SingletonHolder.instance;
}
private static class SingletonHolder {
private static SingletonSerializeFix instance = new SingletonSerializeFix();
}
private Object readResolve() {
return SingletonHolder.instance;
}
}