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