## Java专题二十一:序列化与反序列化
[TOC]
> 用于保存对象的状态并持久化,如:保存到磁盘、在网络上传输等
- 序列化:对象转换成字节序列的过程
- 反序列化:字节序列转换成对象的过程
### 20.1. 实现方法
- 对象序列化必须实现`java.io.Serializable`接口,否则做序列化和反序列化操作中会抛出`java.io.NotSerializableException`异常
- 因为`java.io.Serializable`是一个空的接口,只是用来标识对象可序列化,所以实现序列化操作,对象还需额外实现`writeObject`、`readObject`方法,如`java.util.ArrayList`中
```
public class ArrayList
implements java.io.Serializable {
private static final long serialVersionUID = 8683452581122892189L;
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioral compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
s.readInt(); // ignored
if (size > 0) {
// like clone(), allocate array based upon size not capacity
SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, size);
Object[] elements = new Object[size];
// Read in all elements in the proper order.
for (int i = 0; i < size; i++) {
elements[i] = s.readObject();
}
elementData = elements;
} else if (size == 0) {
elementData = EMPTY_ELEMENTDATA;
} else {
throw new java.io.InvalidObjectException("Invalid size: " + size);
}
}
}
```
- `java.io.ObjectOutputStream`用于将对象写入字节流中,一些重要方法如下:
| 方法 | 说明 |
| --- | --- |
| `void defaultWriteObject()`<br> `throws IOException` | 写入当前类的非static、非transient字段到流中 |
| `void writeObject(Object obj)`<br> `throws IOException` | 写入特定的对象到流中 |
| `void writeInt(int val)`<br> `throws IOException` | 写入32bit的int型数据到流中 |
| `void writeChars(String s) throws IOException` | 写入字符数据到流中 |
- `java.io.ObjectInputStream`用于从流中读取对象,一些重要方法如下:
| 方法 | 说明 |
| --- | --- |
| `void defaultReadObject()`<br> `throws IOException, ClassNotFoundException` | 从流中读取非static、非transient字段到当前类中 |
| ` Object readObject()`<br> `throws IOException, ClassNotFoundException` | 将特定的对象写入流中 |
| `int readInt()`<br> `throws IOException, ClassNotFoundException` | 将32bit的int型数据写入流中 |
如在ArrayList中`writeObject`方法:
- `s.defaultWriteObject()`:默认第一步都调用该方法
- `s.writeInt(size)`:先写入数组中的元素个数,便于后面按个数调用`readObject`方法读取数组中每个元素
- `s.writeObject(elementData[i])`:因为ArrayList内部就是一个对象构成的数组,这里遍历数组写入每个元素
`readObject`方法:
- `s.defaultReadObject()`:默认第一步都调用该方法
- `s.readInt()`:读取元素的个数
- `s.readObject()`:读取每个元素
### 20.2. serialVersionUID
serialVersionUID将每个可序列化的类与一个版本号关联起来,因为我们写的类不是一成不变的,会随着需求的变化而改变
如果在反序列化中,反序列化后的类与当前类的serialVersionUID不同时,会抛出InvalidClassException异常
一般声明一个long类型的数值,并用`private`、`static`、`final`修饰:
~~~
private static final long serialVersionUID = 8683452581122892189L;
~~~
### 20.3. transient
用于修饰对象的成员变量,表示不参与序列化过程,不会保存到字节序列中
- JavaCook
- Java专题零:类的继承
- Java专题一:数据类型
- Java专题二:相等与比较
- Java专题三:集合
- Java专题四:异常
- Java专题五:遍历与迭代
- Java专题六:运算符
- Java专题七:正则表达式
- Java专题八:泛型
- Java专题九:反射
- Java专题九(1):反射
- Java专题九(2):动态代理
- Java专题十:日期与时间
- Java专题十一:IO与NIO
- Java专题十一(1):IO
- Java专题十一(2):NIO
- Java专题十二:网络
- Java专题十三:并发编程
- Java专题十三(1):线程与线程池
- Java专题十三(2):线程安全与同步
- Java专题十三(3):内存模型、volatile、ThreadLocal
- Java专题十四:JDBC
- Java专题十五:日志
- Java专题十六:定时任务
- Java专题十七:JavaMail
- Java专题十八:注解
- Java专题十九:浅拷贝与深拷贝
- Java专题二十:设计模式
- Java专题二十一:序列化与反序列化
- 附加专题一:MySQL
- MySQL专题零:简介
- MySQL专题一:安装与连接
- MySQL专题二:DDL与DML语法
- MySQL专题三:工作原理
- MySQL专题四:InnoDB存储引擎
- MySQL专题五:sql优化
- MySQL专题六:数据类型
- 附加专题二:Mybatis
- Mybatis专题零:简介
- Mybatis专题一:配置文件
- Mybatis专题二:映射文件
- Mybatis专题三:动态SQL
- Mybatis专题四:源码解析
- 附加专题三:Web编程
- Web专题零:HTTP协议
- Web专题一:Servlet
- Web专题二:Cookie与Session
- 附加专题四:Redis
- Redis专题一:数据类型
- Redis专题二:事务
- Redis专题三:key的过期
- Redis专题四:消息队列
- Redis专题五:持久化