0、序言
今天第一次更新簡(jiǎn)書(shū)的博客,(心情那個(gè)忐忑?。┓窒硪粋€(gè)最近稍微了解的序列化的知識(shí)。
之前做一個(gè)Android實(shí)時(shí)推送的功能,是用傳統(tǒng)的Socket然后自定義協(xié)議的方式,非常難處理(不要問(wèn)我為什么用這種坑爹的方式,boss一句話,寶寶心里苦?。?/p>
,常常要處理復(fù)雜的數(shù)據(jù)解析,浪費(fèi)大量時(shí)間在處理數(shù)據(jù)的打包和解析。然后因?yàn)橐淮蚊嬖?,突然從面試官口中?tīng)到了Protobuf,突然發(fā)現(xiàn)世間居然還有這么好用的東西,于是鼓起勇氣研究起序列化協(xié)議的相關(guān)東西。
啰嗦這么多,正文開(kāi)始
1、基本概念
我們先了解一下基本的使用和概念。
Java中序列化的基本概念如下:
- 把對(duì)象轉(zhuǎn)換為字節(jié)序列的過(guò)程稱為對(duì)象的序列化
- 把字節(jié)序列恢復(fù)為對(duì)象的過(guò)程稱為對(duì)象的反序列化。
看這個(gè)等于沒(méi)說(shuō),其實(shí)我們可以看一個(gè)例子:
1.1 如何序列化呢?
老板讓收集用戶的個(gè)人信息,包含它的用戶名,密碼和年齡并傳上來(lái),于是,我們定義一個(gè)需要傳遞的對(duì)象如下:
import java.io.Serializable;
class User implements Serializable {
public byte age = 24;
public String name;
public String password;
}
如下的方式,調(diào)用Java本身的ObjectOutputStream就可以將Object輸出到流中,生成一個(gè)二進(jìn)制序列,完成序列化的操作,同樣的,反序列化就是調(diào)用ObjectInputStream對(duì)象的readObject方法。具體我就不再列例子了。
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
class Main {
public static void main(String[] args) {
FileOutputStream fos = new FileOutputStream("temp.out");
ObjectOutputStream oos = new ObjectOutputStream(fos);
User user = new User();
oos.writeObject(user);
oos.flush();
oos.close();
}
}
1.2 還能做什么?
作為一個(gè)通信協(xié)議,如果序列化只能這樣轉(zhuǎn)換,那遠(yuǎn)遠(yuǎn)不夠的。如何應(yīng)對(duì)產(chǎn)品或是老板無(wú)窮無(wú)盡的需求更改呢(你懂的!)
-
安全性?。?!
今天老板跟你說(shuō),“我們這些字段中的密碼不要傳輸吧,都能被解析這不是很不好嗎?”
但是轉(zhuǎn)念一想,我不能刪除這個(gè)字段啊,我要在其他業(yè)務(wù)中用到這個(gè)字段呢,也不能再新建一個(gè)沒(méi)有密碼的類,這不是很沒(méi)必要嘛。
不要方,我們可以給類定義一個(gè)含有transient關(guān)鍵字的字段。這個(gè)關(guān)鍵字賦值后的數(shù)據(jù),不會(huì)再序列化后出現(xiàn),同時(shí)反序列化也不會(huì)解析,麻麻再也不用擔(dān)心你網(wǎng)絡(luò)傳輸中會(huì)被截取了。
import java.io.Serializable;
class User implements Serializable {
public byte age = 24;
public String name;
public tranisent String password;
}
-
兼容性
老板的需求當(dāng)然不會(huì)停的咯,今天老板又來(lái)說(shuō)了,“這個(gè)年齡啊,還是用年份吧,把原來(lái)的刪了吧,之前版本的也要兼容”
這時(shí)候你一臉蒙蔽,寫出了這樣子的版本
import java.io.Serializable;
class User implements Serializable {
public int year = 1992;
public String name;
public tranisent String password;
}
然后,你發(fā)現(xiàn)這個(gè)根本無(wú)法兼容舊版本!
這個(gè)時(shí)候的解決方案就是要用上serialVersionUID,只有使用了自己定義的serialVersionUID,才能使新版本兼容舊版本。
1.3 其他的坑在哪里?
老板給你的需求越來(lái)越大,你這個(gè)數(shù)據(jù)類也會(huì)越來(lái)越復(fù)雜,你會(huì)開(kāi)始繼承父類,開(kāi)始定義其他的數(shù)據(jù),這個(gè)時(shí)候需要注意下面幾點(diǎn):
- 要想將父類對(duì)象也序列化,就需要讓父類也實(shí)現(xiàn)Serializable 接口。如果父類不實(shí)現(xiàn)的話的,就需要有默認(rèn)的無(wú)參的構(gòu)造函數(shù)。
- 序列化不保存靜態(tài)變量,因?yàn)殪o態(tài)變量是屬于類的狀態(tài),在程序開(kāi)啟時(shí)會(huì)統(tǒng)一加載。
- 如果你想自己定義序列化的規(guī)則,可以使用Externalizable
class User implements Externalizable{
int year = 1992;
String name;
String password;
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(name);
out.writeInt(age);
}
@Override
public void readExternal(ObjectIntput intput) throws IOException{
this.name = (String) in.readObject();
this.age = in.readInt();
}
}
注意: Externalizable會(huì)將tranisent 的關(guān)鍵字的信息屏蔽掉其功能,如果使用了Externalizable就不要使用tranisent關(guān)鍵字。
暫時(shí)介紹序列化的概念,下一篇文章介紹一下protobuf的相關(guān)知識(shí)。