序列化是将对象转换为二进制数据的过程。目的是便于传输。
简单使用
实现一个简单的序列化类,需要注意如下几点:
1、需要序列化的类,需要实现Serializable接口;
2、用transient关键字修饰的成员变量不参与序列化,在被反序列后,该成员变量被赋予初始值,如 int 型的值是 0,对象类型的为 null;
3、静态类型的变量,不属于对象的成员,不会参与序列化;
4、用serialVersionUID标识序列化的版本信息。如果不写系统会默认生成,每次类改变时,serialVersionUID会发生变化。反序列化时,读取到serialVersionUID和当前类不匹配会报invalidClassException异常;
5、成员变量为对象类型,也也必须是可序列化的,否则反序列化时会抛出NotSerializableException异常;
6、子类实现序列化,而父类没有实现,则反序列化后,父类的参数会变成默认值;一个类实现序列化接口,其子类也是可序列化的;
7、必须要提供无参构造函数,如果有重写带参构造函数,则要主动提供一个。否则在反序列化时会报错。因为,在反序列化过程中,需要反射调用无参的构造函数来创建对象。
public class Student implements Serializable {
private static final long serialVersionUID = 1;
private String name;
private int age;
public Student() {}
public Student(String name) {
this.name = name;
}
private transient String sex;
private static int number;
private Course course;
}
public class Course implements Serializable {
private static final long serialVersionUID = 1;
private int score;
}
writeObject/readObject实现自定义序列化
当我们在类中提供了writeObject/readObject方法,则可以实现自定义序列化。自定义序列化会覆盖系统默认的序列化。通过自定义序列化可以更好的把控序列化过程。防范未知的风险。
private void writeObject(ObjectOutputStream outputStream) throws IOException {
outputStream.defaultWriteObject();
outputStream.writeObject(name);
outputStream.writeInt(age);
}
private void readObject(ObjectInputStream inputStream) throws
ClassNotFoundException,IOException {
inputStream.defaultReadObject();
name = (String) inputStream.readObject();
age = inputStream.readInt();
}
单例模式与序列化
通过序列化与反序列化可以使单例模式失效。
private SingleInstance copyInstance(SingleInstance obj)
throws IOException, ClassNotFoundException {
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(path));
outputStream.writeObject(obj);
outputStream.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(path));
SingleInstance singleInstance = (SingleInstance) inputStream.readObject();
inputStream.close();
return singleInstance;
}
单例类中提供readResolve方法,在该方法中直接返回单例对象。
public class SingleInstance {
...
private Object readResolve() {
return single;
}
...
}
能够这样解决的原理,是因为在反序列化过程中,先判断是否有自定义的反序列化方法。有的话执行自定义的反序列化函数。
ObjectInputStream.java
private Object readOrdinaryObject(boolean unshared)
throws IOException {
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod()) {
...
Object rep = desc.invokeReadResolve(obj);
...
}
ObjectStreamClass.java
private ObjectStreamClass(final Class<?> cl) {
...
readResolveMethod = getInheritableMethod(
cl, "readResolve", null, Object.class);
...
}
boolean hasReadResolveMethod() {
requireInitialized();
return (readResolveMethod != null);
}
Externalizable接口
Externalizable是Java提供的另一个序列化接口。Externalizable实际上也是继承Serializable接口。通过实现Externalizable接口,实现序列化,需要重写接口提供的writeExternal和readExternal方法。类似于Serializable的自定义序列化方案。
public interface Externalizable extends java.io.Serializable {
void writeExternal(ObjectOutput out) throws IOException;
void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}
Externalizable序列化方案简单使用,重写writeExternal/readExtrenal定义序列化方案,使用ObjectOutputStream/ObjectInputStream进行序列化和反序列化与Serializable方案一致。
public class StudentExt implements Externalizable {
private String name;
private int age;
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(name);
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in) throws ClassNotFoundException, IOException {
name = (String) in.readObject();
age = in.readInt();
}
}