Java 序列化和反序列化 笔记
概念及作用:
序列化:将Java对象转化为二进制字节流。
反序列化:将二进制字节流转化为Java对象。
作用:序列化就是一类对象的集合,很多的对象数据,这些数据中,有些信息我们想让它长时间的保存起来,于是就进行序列化。序列化就是把内存里面的这些对象给变成一连串的字节描述,保存在文件之中。反序列化就是把保存在文件里面的东西解析还原成对象。
序列化的过程:
方法有两种:继承Serializable接口(隐式序列化)和继承Externalizable接口(显示序列化)。
只有实现了Serializable和Externalizable接口的类的对象才能进行序列化操作。Externalizable接口继承自Serializable口,实现Externalizable接口的类需要自行控制序列化的行为。实现Serializable接口的类采用默认的序列化方式。
Serializable和Externalizable接口的区别:
- 如果一个类实现了Serializable,但他的父类并不是可序列化的,那么该父类必须要有个无参构造函数。
- 如果一个类实现了Externalizable,此类必须有公有无参构造函数以便在反序列化时调用。
- Serializable接口没有定义方法,也没有定义任何常量。如果有类实现了Serializable接口,则等于告诉JVM此类是可序列化的。对于实现了Serializable接口的类,如果需要类的某个成员变量在序列化时被排除,不参与序列化,可在定义成员变量时,使用transient关键词。
- Externalizable接口提供的writeExternal和readExternal两个方法,给予开发者序列化实现的可控。在实现Externalizable接口时,writeExternal,readExternal两方法的传入参数ObjectOutputStream和ObjectInputStream,方法内只能调用他们的writeObject和readObject方法,其他方法无法完成序列化。
两个流对象的作用ObjectInputStream和ObjectOutputStream:
public static void writeObjec(File f) throws IOException{
FileOutputStream outputStream = new FileOutputStream(f);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(new Student());
objectOutputStream.close();
}
public static void readObject(File f) throws IOException, ClassNotFoundException{
FileInputStream inputStream = new FileInputStream(f);
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
Student s2 = (Student) objectInputStream.readObject();
objectInputStream.close();
}
ObjectOutputStream按照默认方式进行序列化时,具有以下特点:
ObjectOutputStream只能对实现了Serializable接口的类的对象进行默认的序列化操作,这种操作仅仅为对象的非transient和非static的实例属性进行序列化。静态属性属于类,不属于对象。静态属性在类加载的时候初始化,将含有静态属性的对象的序列化和反序列化分开在不同的进程中,以得到正确结果。
ObjectInputStream按照默认方式进行反序列化时,具有以下特点:
- 如果内存中对象所属的类还没有加载,那么会加载并初始化这个类。如果在classpath中找不到相应的类文件,抛出ClassNotFoundException。
- 在反序列化时不会调用类的任何构造方法。
序列化注意的问题:
- 序列化和反序列化的顺序要一致
- 保存对象的文件名一般不要用.txt文件,换成.obj或.ser文件最好
Transient成员属性:
transient属性不能被序列化,可以修饰以下类型的属性。
- 实例属性不代表对象的固有的内部数据,仅仅代表具有一定逻辑含义的临时数据。
private String firstName;
private String lastName;
private transient String fullName; - 实例属性表示一些比较敏感的信息(密码等), 出于安全方面的原因不希望被序列化。
- 实例属性需要按照用户自定义的方式序列化,如经过加密后再序列化。
具有关联关系的对象之间的序列化:系统还会序列化此类所关联的其他可序列化的对象。
自定义Serializable接口:
自定义序列化方式,在可序列化类中定义下面两个方法:
private void writeObject(ObjectOutputStream out) throws IOException
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundExce ption
自定义Externalizable接口:
Externalizable接口继承自Serializable接口。如果一个类实现了Externalizable接口,那么将完全由这个类控制自身的序列化行为。
public void writeExternal(ObjectOutput out)throws IOException
public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException
对实现了Externalizable接口的类的对象进行反序列化操作时,会调用该类无参的构造方法。
默认序列化与自定义序列化的比较:
默认序列化方式的不足:
- 对象当中的不易对外公开的敏感数据进行序列化,安全性低。
- 不会检查对象的成员属性是否合乎正确的约束条件。
- 默认的序列化方式需要对对象图进行递归遍历,如果对象图很复杂,会消耗很多空间和时间,甚至引起Java虚拟机堆栈溢出。
自定义序列化方式两种:
- 实现Serializable接口,并且提供private的writeObject()和readObject()。
- 实现Externalizable接口,实现writeExternal()和readExternal()方法,必须提供public无参的构造方法。