文章目录
- 前言
- 对象流
- 序列化:
- 序列化的特点:
- serialVersionUID:序列化版本号
- 序列化的过程
- 反序列化:
- 反序列化的过程:
- 补充:
前言
使用对象和磁盘打交道的IO类是ObjectInputStream和ObjectOutputStream
而序列化和反序列化是对象是否可以使用对象流的关键
对象流
ObjectInputStream:对象输入流:存入到磁盘中一个对象实体
ObjectOutputStream:对象输出流:从瓷盘中取出一个对象实体
注意:对象流操作的对象需要 实现序列化。
序列化:
将对象实例保存到磁盘的过程就是序列化(ObjectOutputStream)
序列化的特点:
1、对象要能完成序列化和反序列化的对象必须实现Serializable接口
2、序列化和反序列化需要使用到IO类ObjectInputStream和ObjectOutputStream
3、虚拟机是否能支持序列化,也需要取决于serialVersionUID值
4、transient关键字是来控制变量的序列化,在变量前加关键,当前的变量不参与序列化,反序列化中 transient关键字修饰的变量会给定初始值
如:int类型0,对象类型给定null值
5、要将父类对象也参与序列化,则就需要让父类对象实现实现Serializable接口
serialVersionUID:序列化版本号
serialVersionUID是一个标识符,通常在对象的哈希码序列化时会标记在对象上
serialVersionUID生成方式:
1、一般可以是默认值1
2、另一种是类名、接口名、成员方法和属性等生成的64位的哈希字段
作用是进行版本控制,当对类添加或者修改类中的任何字段,已经序列化的对象将无法恢复,因为类已经生成新的serialVersionUID,在进行反序列化时,会将当前类的serialVersionUID和序列化的对象的serialVersionUID进行比较,如果不匹配,将抛出异常
序列化的过程
ObjectOutputStream outputStream = null;
try {
outputStream = new ObjectOutputStream(new FileOutputStream(path));
outputStream.writeObject(o);
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
反序列化:
将对象从磁盘中读取出来的过程是反序列化(ObjectInputStream)
反序列化的过程:
ObjectInputStream inputStream = null;
try {
inputStream = new ObjectInputStream(new FileInputStream(path));
Person person = (Person) inputStream.readObject();
System.out.println(person);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
补充:
为什么Serializable接口没有任何方法可以完成序列化:
我们通过查看ArrayList源码来进行分析
在序列化过程中,如果被序列化的类实现了writeObject和readObject方法,虚拟机在调用对象类型的readObject 和writeObject是,会执行到该类中的自定义的方法,如果类里面没实现自定义的方法的话,ObjectOutputStream会调用默认的defaultWriteObject,或者ObjectInputStream中的defaultInputStream方法
if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum) obj, desc, unshared);
} else if (obj instanceof Serializable) {
//判定当前是Serializable实现类
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
Serializable接口提供一个标识,方便底层序列化中调用特定的方法
transient:的作用:
比如:ArrayList中的elementDate属性使用transient关键字修饰
ArrayList是一个动态数组,每次在放满后都会动态的扩容设定一个长度值,如果设定数据长度为100,而实际存放1个有效数据,那么会序列化99个null元素,为了保证序列化不将99个null值参与序列计划,ArrayList设定为transient
elementDate在序列化是是将数据取出来进行序列化的