摘要:的序列化是將一個(gè)對象表示成字節(jié)序列,該字節(jié)序列包括了對象的數(shù)據(jù),有關(guān)對象的類型信息和存儲在對象中的數(shù)據(jù)類型。這個(gè)是根據(jù)類名接口名成員方法及屬性等來生成一個(gè)位的哈希字段,因?yàn)樵黾恿俗侄危虼松傻牟灰粯恿恕?/p>
Java序列化 什么是序列化?
序列化是將一個(gè)對象的狀態(tài),各屬性的值序列化保存起來,然后在合適的時(shí)候通過反序列化獲得。
Java的序列化是將一個(gè)對象表示成字節(jié)序列,該字節(jié)序列包括了對象的數(shù)據(jù),有關(guān)對象的類型信息和存儲在對象中的數(shù)據(jù)類型。
說白了,就是將對象保存起來,就跟保存字符串?dāng)?shù)據(jù)一樣,用到的時(shí)候再取出來。任何實(shí)現(xiàn)了Serializable接口的類都可以被序列化。
實(shí)現(xiàn)Serializable接口進(jìn)行序列化package com.wangjun.othersOfJava; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class SerializeDemo { public static void main(String[] args) { Employee em = new Employee(); em.name = "wangjun"; em.age = 24; em.ssh = 123456; // 將對象序列化后保存到文件 try ( FileOutputStream fo = new FileOutputStream("tem.ser"); ObjectOutputStream oo = new ObjectOutputStream(fo)) { oo.writeObject(em); } catch (IOException e) { e.printStackTrace(); } // 反序列化取出對象 try( FileInputStream fi = new FileInputStream("tem.ser"); ObjectInputStream oi = new ObjectInputStream(fi)) { Employee e2 = (Employee) oi.readObject(); System.out.println(e2.name); System.out.println(e2.age); System.out.println(e2.ssh); System.out.println(Employee.local); e2.test(); } catch (Exception e) { e.printStackTrace(); } } static class Employee implements Serializable { String name; int age; static String local = "earth"; transient int ssh; public void test() { System.out.println("this is test method!"); } } }
程序的運(yùn)行結(jié)果:
wangjun 24 0 earth this is test method!
如果有一些字段不想被序列化怎么辦呢?這時(shí)候就可以用transient關(guān)鍵字修飾,就像上面代碼的ssh字段,關(guān)于transient關(guān)鍵字有以下幾個(gè)特點(diǎn):
一旦被transient關(guān)鍵字修飾,那變量將不再是對象持久化的一部分,該變量內(nèi)容在序列化后無法獲得訪問;
transient只能修飾變量,不能修飾方法和類,本地變量(局部變量)也不能被transient修飾;
一個(gè)靜態(tài)變量不管是否被transient修飾,都不能被序列化。
從上面的例子看到好像與第三條不符,其實(shí)反序列化取出的local是JVM里面的值,而不是反序列化出來的??梢约右恍写a驗(yàn)證一下,在反序列化之前更改一下local的值:
// 反序列化取出對象 Employee.local = "earth2"; try( ...
看一下打印結(jié)果
wangjun 24 0 earth2 this is test method!
這說明打印出來的是JVM中對應(yīng)的local的值earth2,而不是序列化的時(shí)候的值earth。
實(shí)現(xiàn)Externalizable接口進(jìn)行序列化transient只有對實(shí)現(xiàn)了Serializable接口方式的序列化有效,還有一種序列化的方式是實(shí)現(xiàn)Externalizable接口,這種實(shí)現(xiàn)方式不像實(shí)現(xiàn)Serializable接口一樣可以幫你自動(dòng)序列化,它需要在writeExternal方法中手動(dòng)指定需要序列化的變量并且在readExternal手動(dòng)取出來,這與是否被transient修飾無關(guān),下面更改一下上面的例子,將Employee類改成:
static class Employee implements Externalizable { String name; int age; static String local = "earth"; transient int ssh; //實(shí)現(xiàn)Externalizable接口進(jìn)行序列化必須顯式聲明無參構(gòu)造器 public Employee() { } public void test() { System.out.println("this is test method!"); } @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(name); //out.writeObject(age); out.writeObject(ssh); out.writeObject(local); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { name = (String) in.readObject(); //age = (int) in.readObject(); ssh = (int) in.readObject(); local = (String) in.readObject(); } }
重新運(yùn)行,結(jié)果(注意:上述主函數(shù)中還存在對local重新賦值的代碼Employee.local = "earth2";):
wangjun 0 123456 earth this is test method!
可以看到能否被序列化跟transient和static修飾都沒有關(guān)系,只跟writeExternal和readExternal有關(guān)系。
Serializable和Externalizable的區(qū)別對Serializable對象反序列化時(shí),由于Serializable對象完全以它存儲的二進(jìn)制位為基礎(chǔ)來構(gòu)造,因此并不會調(diào)用任何構(gòu)造函數(shù),因此Serializable類無需默認(rèn)構(gòu)造函數(shù),但是當(dāng)Serializable類的父類沒有實(shí)現(xiàn)Serializable接口時(shí),反序列化過程會調(diào)用父類的默認(rèn)構(gòu)造函數(shù),因此該父類必需有默認(rèn)構(gòu)造函數(shù),否則會拋異常。
對Externalizable對象反序列化時(shí),會先調(diào)用類的不帶參數(shù)的構(gòu)造方法,這是有別于默認(rèn)反序列方式的。如果把類的不帶參數(shù)的構(gòu)造方法刪除,或者把該構(gòu)造方法的訪問權(quán)限設(shè)置為private、默認(rèn)或protected級別,會拋出java.io.InvalidException: no valid constructor異常,因此Externalizable對象必須有默認(rèn)構(gòu)造函數(shù),而且必需是public的。
如果不是特別堅(jiān)持實(shí)現(xiàn)Externalizable接口,那么還有另一種方法。我們可以實(shí)現(xiàn)Serializable接口,并添加writeObject()和readObject()的方法。一旦對象被序列化或者重新裝配,就會分別調(diào)用那兩個(gè)方法。也就是說,只要提供了這兩個(gè)方法,就會優(yōu)先使用它們,而不考慮默認(rèn)的序列化機(jī)制。
SerialVersionUID的作用上述實(shí)現(xiàn)Serializable接口的Employee類中,會有一個(gè)警告:
The serializable class Employee does not declare a static final serialVersionUID field of type long
意思是Employee沒有聲明一個(gè)靜態(tài)final的常量serialVersionUID,那這個(gè)serialVersionUID的作用是什么呢?
serialVersionUID是對類進(jìn)行版本控制的,Java的序列化機(jī)制是通過判斷類的serialVersionUID來驗(yàn)證版本一致性的。在進(jìn)行反序列化時(shí),JVM會把傳來的字節(jié)流中的serialVersionUID與本地相應(yīng)實(shí)體類的serialVersionUID進(jìn)行比較,如果相同就認(rèn)為是一致的,可以進(jìn)行反序列化,否則就會出現(xiàn)序列化版本不一致的異常,即是InvalidCastException。
serialVersionUID有兩種生成方式:
一是默認(rèn)的1L,比如:private static final long serialVersionUID = 1L;
二是根據(jù)類名、接口名、成員方法及屬性等來生成一個(gè)64位的哈希字段。
如果程序沒有顯式的聲明serialVersionUID,那么程序?qū)⒂玫诙N實(shí)現(xiàn)。我們可以做一個(gè)實(shí)現(xiàn),還是用上述實(shí)現(xiàn)Serializable接口的例子。
我們先運(yùn)行一下程序,生成序列化文件tem.ser,在把“將對象序列化后保存到文件”這一段邏輯注釋掉,對Employee類增加一個(gè)test字段:
static class Employee implements Serializable { String name; int age; static String local = "earth"; transient int ssh; String test; public void test() { System.out.println("this is test method!"); } }
這時(shí)候運(yùn)行的時(shí)候會報(bào)錯(cuò):
java.io.InvalidClassException: com.wangjun.othersOfJava.SerializeDemo$Employee; local class incompatible: stream classdesc serialVersionUID = 4506166831890198488, local class serialVersionUID = 785960679919880606 at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1843) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1713) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2000) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1535) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422) at com.wangjun.othersOfJava.SerializeDemo.main(SerializeDemo.java:32)
因?yàn)槌绦虬l(fā)現(xiàn)取到的序列化文件的serialVersionUID和當(dāng)前的serialVersionUID不一樣。這個(gè)serialVersionUID是根據(jù)類名、接口名、成員方法及屬性等來生成一個(gè)64位的哈希字段,因?yàn)樵黾恿藅est字段,因此生成的serialVersionUID不一樣了。
接著,我們顯式的聲明serialVersionUID
static class Employee implements Serializable { private static final long serialVersionUID = 1L; String name; int age; static String local = "earth"; transient int ssh; public void test() { System.out.println("this is test method!"); } }
將剛才注釋的代碼取消注釋,運(yùn)行一遍再注釋掉,并且新增字段test:
static class Employee implements Serializable { private static final long serialVersionUID = 1L; String name; int age; static String local = "earth"; transient int ssh; String test; public void test() { System.out.println("this is test method!"); } }
再次運(yùn)行發(fā)現(xiàn)沒有報(bào)錯(cuò),運(yùn)行OK。這是因?yàn)槟泔@式聲明了serialVersionUID,序列化的serialVersionUID和目前的serialVersionUID一樣,因此會認(rèn)為是同一個(gè)版本的類。
你也可以將serialVersionUID改成2L,這個(gè)時(shí)候又會報(bào)錯(cuò)了。
參考:https://www.cnblogs.com/duanx...
https://blog.csdn.net/fjndwy/...
https://blog.csdn.net/bigtree...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/71122.html
摘要:談任何技術(shù)前,不討論實(shí)際的應(yīng)用場景都是耍流氓。業(yè)務(wù)只讀不寫,且數(shù)據(jù)量不大,這兩個(gè)特性很大程度上決定了我們的系統(tǒng)架構(gòu)。 談任何技術(shù)前,不討論實(shí)際的應(yīng)用場景都是耍流氓。 首先說下自身項(xiàng)目背景。業(yè)務(wù)只讀不寫,且數(shù)據(jù)量不大,這兩個(gè)特性很大程度上決定了我們的系統(tǒng)架構(gòu)。 Step 0 數(shù)據(jù)平臺持久化數(shù)據(jù)到數(shù)據(jù)庫(SQLServer)中,然后壓縮數(shù)據(jù)庫文件成壓縮文件Step 1 上傳壓縮文...
摘要:哪吒社區(qū)技能樹打卡打卡貼函數(shù)式接口簡介領(lǐng)域優(yōu)質(zhì)創(chuàng)作者哪吒公眾號作者架構(gòu)師奮斗者掃描主頁左側(cè)二維碼,加入群聊,一起學(xué)習(xí)一起進(jìn)步歡迎點(diǎn)贊收藏留言前情提要無意間聽到領(lǐng)導(dǎo)們的談話,現(xiàn)在公司的現(xiàn)狀是碼農(nóng)太多,但能獨(dú)立帶隊(duì)的人太少,簡而言之,不缺干 ? 哪吒社區(qū)Java技能樹打卡?【打卡貼 day2...
摘要:例如在周上設(shè)置表示周一三五觸發(fā)用于遞增觸發(fā)。例如在周上設(shè)置,表示本月的最后一個(gè)星期五表示離指定日期最近那個(gè)工作日周一至周五,例如在日字段上設(shè)置,表示離每月號最近的那個(gè)工作日觸發(fā)。相關(guān)例子代碼較簡單的定時(shí)任務(wù)調(diào)度工具利用集成 筆記來源:IMOOC Java Quartz Quartz 簡介 OpenSymphony 提供的強(qiáng)大的開源任務(wù)調(diào)度框架 純 Java 實(shí)現(xiàn),精細(xì)控制排程 Q...
摘要:與類基本相同,都是可變字符換字符串序列,不同點(diǎn)是是線程安全的,是線程不安全的。和區(qū)別在大部分情況下是線程安全的可變字符序列。在程序中可將字符串緩沖區(qū)安全地用于多線程。 轉(zhuǎn)載自飄過的小牛 我們先要記住三者的特征: String 字符串常量 StringBuffer 字符串變量(線程安全) StringBuilder 字符串變量(非線程安全) 一、定義 showImg(/...
摘要:遇到問題查查,看看,大神的講解問問島胖君下面是我最近整理出來的關(guān)于字符串的文章的怎么翻譯匯集目錄非常希望強(qiáng)化博客的功能,比如分類,置頂。 雖是讀書筆記,但是如轉(zhuǎn)載請注明出處 http://segmentfault.com/blog/exploring/ .. 拒絕伸手復(fù)制黨 最近在看算法和語言,基本屬于看知識 --> java實(shí)現(xiàn) --> 整理blog 這個(gè)路線。 遇到問題查查st...
閱讀 3941·2021-10-12 10:12
閱讀 2900·2021-09-10 11:18
閱讀 3685·2019-08-30 15:54
閱讀 2817·2019-08-30 15:53
閱讀 652·2019-08-30 13:54
閱讀 981·2019-08-30 13:21
閱讀 2270·2019-08-30 12:57
閱讀 1700·2019-08-30 11:10