## 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專題五:持久化