Android序列化問題與思考
阿新 • • 發佈:2020-11-12
今天再來談談Android中的物件序列化,你瞭解多少呢?
## 序列化指的是什麼?有什麼用
**序列化**指的是講物件變成有序的`位元組流`,變成位元組流之後才能進行傳輸儲存等一系列操作。
**反序列化**就是序列化的`相反操作`,也就是把序列化生成的位元組流轉為我們記憶體的物件。
## 介紹下Android中兩種序列化介面
### Serializable
是`Java`提供的一個序列化介面,是一個空介面,專門為物件提供序列化和反序列化操作。具體使用如下:
```java
public class User implements Serializable {
private static final long serialVersionUID=519067123721561165l;
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
```
實現`Serializable`介面,宣告一個`serialVersionUID`。
到這裡可能有人就問了,不對啊,平時沒有這個`serialVersionUID`啊。沒錯,`serialVersionUID`不是必須的,因為不寫的話,系統會自動生成這個變數。它有什麼用呢?當序列化的時候,系統會把當前類的`serialVersionUID`寫入序列化的檔案中,當反序列化的時候會去檢測這個`serialVersionUID`,看他是否和當前類的`serialVersionUID`一致,一樣則可以正常反序列化,如果不一樣就會報錯了。
所以這個`serialVersionUID`就是序列化和反序列化過程中的一個標識,代表一致性。不加的話會有什麼影響?如果我們序列化後,改動了這個類的某些成員變數,那麼`serialVersionUID`就會改變,這時候再拿之前序列化的資料來反序列化就會報錯。所以如果我們手動指定`serialVersionUID`就能保證最大限度來恢復資料。
`Serializable`的實質其實是是把Java物件序列化為二進位制檔案,然後就能在程序之間傳遞,並且用於網路傳輸或者本地儲存等一系列操作,因為他的本質就儲存了檔案。可以看看原始碼:
```java
private void writeObject0(Object obj, boolean unshared)
throws IOException
{
...
try {
Object orig = obj;
Class cl = obj.getClass();
ObjectStreamClass desc;
desc = ObjectStreamClass.lookup(cl, true);
if (obj instanceof Class) {
writeClass((Class) obj, unshared);
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
// END Android-changed: Make Class and ObjectStreamClass replaceable.
} else 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) {
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
}
...
}
private void writeOrdinaryObject(Object obj,
ObjectStreamClass desc,
boolean unshared)
throws IOException
{
...
try {
desc.checkSerialize();
//寫入二進位制檔案,普通物件開頭的魔數0x73
bout.writeByte(TC_OBJECT);
//寫入對應的類的描述符,見底下原始碼
writeClassDesc(desc, false);
handles.assign(unshared ? null : obj);
if (desc.isExternalizable() && !desc.isProxy()) {
writeExternalData((Externalizable) obj);
} else {
writeSerialData(obj, desc);
}
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
public long getSerialVersionUID() {
// 如果沒有定義serialVersionUID,序列化機制就會呼叫一個函式根據類內部的屬性等計算出一個hash值
if (suid == null) {
suid = AccessController.doPrivileged(
new Privileg