摘要:引語(yǔ)平時(shí)我們?cè)谶\(yùn)行程序的時(shí)候創(chuàng)建的對(duì)象都在內(nèi)存中當(dāng)程序停止或者中斷了對(duì)象也就不復(fù)存在了如果我們能將對(duì)象保存起來(lái)在需要使用它的時(shí)候在拿出來(lái)使用就好了并且對(duì)象的信息要和我們保存時(shí)的信息一致序列化就可以解決了這樣的問(wèn)題序列化當(dāng)然不止一種方式如下序
引語(yǔ):
????平時(shí)我們?cè)谶\(yùn)行程序的時(shí)候,創(chuàng)建的對(duì)象都在內(nèi)存中,當(dāng)程序停止或者中斷了,對(duì)象也就不復(fù)存在了.如果我們能將對(duì)象保存起來(lái),在需要使用它的時(shí)候在拿出來(lái)使用就好了,并且對(duì)象的信息要和我們保存
時(shí)的信息一致.序列化就可以解決了這樣的問(wèn)題.序列化當(dāng)然不止一種方式,如下:
序列類型 | 是否跨語(yǔ)言 | 優(yōu)缺點(diǎn) |
---|---|---|
hession | 支持 | 跨語(yǔ)言,序列化后體積小,速度較快 |
protostuff | 支持 | 跨語(yǔ)言,序列化后體積小,速度快,但是需要Schema,可以動(dòng)態(tài)生成 |
jackson | 支持 | 跨語(yǔ)言,序列化后體積小,速度較快,且具有不確定性 |
fastjson | 支持 | 跨語(yǔ)言支持較困難,序列化后體積小,速度較快,只支持java,c# |
kryo | 支持 | 跨語(yǔ)言支持較困難,序列化后體積小,速度較快 |
fst | 不支持 | 跨語(yǔ)言支持較困難,序列化后體積小,速度較快,兼容jdk |
jdk | 不支持 | 序列化后體積很大,速度快 |
我們今天介紹的就是java原生的Serializable序列化.
先列一下概念:
序列化:序列化是將對(duì)象的狀態(tài)信息轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)男问降倪^(guò)程
反序列化:從存儲(chǔ)或傳輸形式還原為對(duì)象
序列化使用起來(lái)很簡(jiǎn)單只需要實(shí)現(xiàn)Serializable接口即可,然后序列化(序列化是將對(duì)象的狀態(tài)信息轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)男问降倪^(guò)程)和反序列化(反之,從存儲(chǔ)或傳輸形式還原為對(duì)象).
只要使用ObjectOutputStream和ObjectInputStream將對(duì)象轉(zhuǎn)為二進(jìn)制序列和還原為java對(duì)象.話不多說(shuō),看下代碼示例:
private static void testSerializable(String fileName) throws IOException { try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(fileName))) { // "XXX" 的String也可以直接作為對(duì)象進(jìn)行反序列化的 objectOutputStream.writeObject("test serializable"); SerializableData data = new SerializableData(1, "testStr"); objectOutputStream.writeObject(data); } } private static void testDeserializable(String fileName) throws IOException, ClassNotFoundException { try (ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(fileName))) { String str = (String) objectInputStream.readObject(); System.out.println("String的反序列化: " + str); SerializableData readData = (SerializableData) objectInputStream.readObject(); System.out.println("反序列化的對(duì)象: " + readData.toString()); // 輸出:反序列化的對(duì)象: SerializableData(testInt=1, testStr=testStr) } } // 使用到的類 @Data @AllArgsConstructor class SerializableData implements Serializable { private Integer testInt; private String testStr; }
第一個(gè)方法是傳入文件路徑,將String和SerializableData對(duì)象序列化到fileName指定的文件中;第二個(gè)方法是反序列化將文件中的二進(jìn)制還原為java對(duì)象.
這里其實(shí)比較簡(jiǎn)單沒(méi)有什么大問(wèn)題,稍微提一句的就是writeObject這個(gè)方法是可以直接將"寫入的字符串"這種形式的對(duì)象直接序列化為二進(jìn)制的.
這里還有一點(diǎn)就是反序列化的版本號(hào)必須和原本對(duì)象的版本號(hào)(private static final long serialVersionUID = 1L;這個(gè)因?yàn)槭亲约簻y(cè)試所以沒(méi)有寫默認(rèn)是1L,修改后,反序列化的對(duì)象版本號(hào)不一致會(huì)報(bào)錯(cuò))一致,并且jvm能找到反序列化的文件的位置,否則都會(huì)失敗.
簡(jiǎn)單的使用序列化和反序列化應(yīng)該沒(méi)有什么問(wèn)題,我們?cè)賮?lái)看看transient關(guān)鍵字是啥?在某些場(chǎng)景下,我們需要寫入或者還原的數(shù)據(jù)中其實(shí)有我們不需要透露或者說(shuō)不想暴露給外部的數(shù)據(jù),如果我們將這些隱私的數(shù)據(jù)序列化,在反序列化出來(lái),
那么這些信息就泄漏了.而transient關(guān)鍵字呢,就是防止這種事情的發(fā)生.
當(dāng)屬性被加上了transient關(guān)鍵字以后,序列化時(shí)不會(huì)將該屬性的值給寫入,所以反序列化的時(shí)候我們會(huì)發(fā)現(xiàn)原本寫入的數(shù)據(jù),還原出來(lái)是null.
我們寫一個(gè)例子看看是否是這樣呢?
private static void testTransient() throws IOException, ClassNotFoundException { String fileName = "transientData.txt"; try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(fileName))) { Account data = new Account(1, "user1", "123456"); out.writeObject(data); } try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(fileName))) { Account readData = (Account) in.readObject(); System.out.println("transient關(guān)鍵字的對(duì)象: " + readData.toString()); // 輸出: transient關(guān)鍵字的對(duì)象: Account(id=1, userName=user1, idCardNumber=null) } } // 對(duì)應(yīng)的對(duì)象 @Data @AllArgsConstructor class Account implements Serializable { private Integer id; private String userName; private transient String idCardNumber; }
這里我們有一個(gè)Account對(duì)象,我們不想暴露出我們的省份證號(hào)碼idCardNumber,于是乎加上了transient關(guān)鍵字.
然后將idCardNumber已經(jīng)初始化過(guò)的data對(duì)象序列化,當(dāng)我們?cè)俜葱蛄谢ト〉眠@個(gè)idCardNumber的值的時(shí)候,發(fā)現(xiàn)確實(shí)對(duì)象的idCardNumber是null,transient是起作用的.
如果是對(duì)基本類型數(shù)據(jù)加上transitent話,會(huì)得到對(duì)應(yīng)的默認(rèn)值,就好比是int的數(shù)據(jù)類型,得到的就是0.
使用過(guò)自動(dòng)的序列化和反序列化以后,我們又想在序列化和反序列化的時(shí)候我們能不能自己控制呢?在序列化和反序列化的時(shí)候我們能不能加點(diǎn)日志或者其他的操作之類的呢?
是的,闊以的.只需要輕輕一點(diǎn),實(shí)現(xiàn)Externalizable接口即可,和Serializable使用差不多.
private static void testExternalizable() throws IOException, ClassNotFoundException { String fileName = "testExternalizable.txt"; try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(fileName))) { Account2 data = new Account2("user1", 1); out.writeObject(data); } try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(fileName))) { Account2 readData = (Account2) in.readObject(); System.out.println("Externalizable的對(duì)象: " + readData.toString()); } } // 使用到的對(duì)象 @Data @AllArgsConstructor class Account2 implements Externalizable { private Integer id; private String userName; private transient String idCardNumber; @Override public void writeExternal(ObjectOutput out) throws IOException { System.out.println("執(zhí)行了writeExternal方法"); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { System.out.println("執(zhí)行了readExternal方法"); } }
如果執(zhí)行了上面的代碼,恭喜你,獲得一個(gè)Exception的獎(jiǎng)勵(lì).大概長(zhǎng)這樣,java.io.InvalidClassException:XXX no valid constructor,
Externalizable在執(zhí)行的時(shí)候會(huì)調(diào)用默認(rèn)的無(wú)參構(gòu)造函數(shù),而且記住哦,必須是public的,如果沒(méi)有加public你會(huì)發(fā)現(xiàn)又獎(jiǎng)勵(lì)了一個(gè)Exception給你.
講道理這個(gè)是比較坑的.下面我們來(lái)看看正確的用法,序列化和反序列化都是我們自己控制的:
private static void testExternalizable() throws IOException, ClassNotFoundException { String fileName = "testExternalizable.txt"; try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(fileName))) { Account3 data = new Account3("user1", 1); out.writeObject(data); } try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(fileName))) { Account3 readData = (Account3) in.readObject(); System.out.println("Externalizable的對(duì)象: " + readData.toString()); /** * 輸出: * 執(zhí)行了writeExternal方法 * 執(zhí)行了readExternal方法 * Externalizable的對(duì)象: Account3(userName=user1, id=1) */ } } @ToString class Account3 implements Externalizable { private String userName; private Integer id; public Account3() { } public Account3(String userName, Integer id) { this.userName = userName; this.id = id; } @Override public void writeExternal(ObjectOutput out) throws IOException { System.out.println("執(zhí)行了writeExternal方法"); out.writeObject(userName); out.writeInt(id); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { System.out.println("執(zhí)行了readExternal方法"); userName = (String) in.readObject(); id = in.readInt(); } }總結(jié):
1.我們介紹了jdk自帶的序列化和反序列化(和其中的一些坑點(diǎn));
2.知道了transient可以將隱私數(shù)據(jù)不序列化;
3.還有Externalizable可以自己來(lái)控制序列化和反序列化的進(jìn)程.
1.https://docs.oracle.com/javas...
2.https://blog.csdn.net/do_bset...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/75523.html
摘要:從的序列化和反序列化說(shuō)起序列化是將對(duì)象的狀態(tài)信息轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)男问降倪^(guò)程,而相反的過(guò)程就稱為反序列化。當(dāng)使用接口來(lái)進(jìn)行序列化與反序列化的時(shí)候需要開發(fā)人員重寫與方法。 從java的序列化和反序列化說(shuō)起 序列化 (Serialization)是將對(duì)象的狀態(tài)信息轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)男问降倪^(guò)程,而相反的過(guò)程就稱為反序列化。 在java中允許我們創(chuàng)建可復(fù)用的對(duì)象,但是這些對(duì)象僅僅存在j...
摘要:序列化對(duì)象和平臺(tái)無(wú)關(guān),序列化得到的字節(jié)流可以在任何平臺(tái)反序列化。從文件中或網(wǎng)絡(luò)上獲得序列化的字節(jié)流后,根據(jù)字節(jié)流中所保存的對(duì)象狀態(tài)及描述信息,通過(guò)反序列化重建對(duì)象。因此意味著不要序列化靜態(tài)變量不屬于對(duì)象狀態(tài)的一部分,因此它不參與序列化。 一.序列化和反序列化(1)序列化:將內(nèi)存中的對(duì)象轉(zhuǎn)化為字節(jié)序列,用于持久化到磁盤中或者通過(guò)網(wǎng)絡(luò)傳輸。對(duì)象序列化的最主要的用處就是傳遞和保存對(duì)象,保證對(duì)...
摘要:序列化反序列化主要體現(xiàn)在程序這個(gè)過(guò)程中,包括網(wǎng)絡(luò)和磁盤。如果是開發(fā)應(yīng)用,一般這兩個(gè)注解對(duì)應(yīng)的就是序列化和反序列化的操作。協(xié)議的處理過(guò)程,字節(jié)流內(nèi)部對(duì)象,就涉及這兩種序列化。進(jìn)行第二步操作,也就是序列化和反序列化的核心是。 以下內(nèi)容,如有問(wèn)題,煩請(qǐng)指出,謝謝! 對(duì)象的序列化/反序列化大家應(yīng)該都比較熟悉:序列化就是將object轉(zhuǎn)化為可以傳輸?shù)亩M(jìn)制,反序列化就是將二進(jìn)制轉(zhuǎn)化為程序內(nèi)部的...
摘要:把字節(jié)序列恢復(fù)為對(duì)象的過(guò)程稱為對(duì)象的反序列化。代表對(duì)象輸入流,它的方法從一個(gè)源輸入流中讀取字節(jié)序列,再把它們反序列化為一個(gè)對(duì)象,并將其返回。接口繼承自接口,實(shí)現(xiàn)接口的類完全由自身來(lái)控制序列化的行為,而僅實(shí)現(xiàn)接口的類可以采用默認(rèn)的序列化方式。 把對(duì)象轉(zhuǎn)換為字節(jié)序列的過(guò)程稱為對(duì)象的序列化。把字節(jié)序列恢復(fù)為對(duì)象的過(guò)程稱為對(duì)象的反序列化。 對(duì)象的序列化主要有兩種用途: 1) 把...
摘要:虛擬機(jī)讀取其他進(jìn)程的數(shù)據(jù)對(duì)象的方法可以運(yùn)行平臺(tái)上的其他程序該方法產(chǎn)生一個(gè)對(duì)象對(duì)象代表由該程序啟動(dòng)啟動(dòng)的子進(jìn)程類提供如下三個(gè)方法用于和其子進(jìn)程通信獲取子進(jìn)程的錯(cuò)誤流獲取子進(jìn)程的輸入流獲取子進(jìn)程的輸出流這里的輸入流輸出流容易混淆從程序的角度思考 Java虛擬機(jī)讀取其他進(jìn)程的數(shù)據(jù) Runtime對(duì)象的exec方法可以運(yùn)行平臺(tái)上的其他程序,該方法產(chǎn)生一個(gè)Process對(duì)象,Process對(duì)象...
閱讀 2854·2021-11-24 10:23
閱讀 1215·2021-11-17 09:33
閱讀 2593·2021-09-28 09:41
閱讀 1513·2021-09-22 15:55
閱讀 3702·2019-08-29 16:32
閱讀 2000·2019-08-29 16:25
閱讀 1111·2019-08-29 11:06
閱讀 3478·2019-08-29 10:55