真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

如何理解序列化

本篇內(nèi)容介紹了“如何理解序列化”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

創(chuàng)新互聯(lián)公司專注于企業(yè)全網(wǎng)營銷推廣、網(wǎng)站重做改版、隆昌網(wǎng)站定制設(shè)計、自適應(yīng)品牌網(wǎng)站建設(shè)、HTML5、商城網(wǎng)站建設(shè)、集團(tuán)公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站制作、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計等建站業(yè)務(wù),價格優(yōu)惠性價比高,為隆昌等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。

 

本文主要內(nèi)容

如何理解序列化

背景

在Java語言中,程序運行的時候,會產(chǎn)生很多對象,而對象信息也只是在程序運行的時候才在內(nèi)存中保持其狀態(tài),一旦程序停止,內(nèi)存釋放,對象也就不存在了。

怎么能讓對象永久的保存下來呢?--------對象序列化 。

何為序列化和反序列化?

  • 序列化:對象到IO數(shù)據(jù)流

如何理解序列化

  • 反序列化:IO數(shù)據(jù)流到對象

如何理解序列化

有哪些使用場景?

Java平臺允許我們在內(nèi)存中創(chuàng)建可復(fù)用的Java對象,但一般情況下,只有當(dāng)JVM處于運行時,這些對象才可能存在,即,這些對象的生命周期不會比JVM的生命周期更長。但在現(xiàn)實應(yīng)用中,就可能要求在JVM停止運行之后能夠保存(持久化)指定的對象,并在將來重新讀取被保存的對象。Java對象序列化就能夠幫助我們實現(xiàn)該功能。

使用Java對象序列化,在保存對象時,會把其狀態(tài)保存為一組字節(jié),在未來,再將這些字節(jié)組裝成對象。必須注意地是,對象序列化保存的是對象的"狀態(tài)",即它的成員變量。由此可知,對象序列化不會關(guān)注類中的靜態(tài)變量。

除了在持久化對象時會用到對象序列化之外,當(dāng)使用RMI(遠(yuǎn)程方法調(diào)用),或在網(wǎng)絡(luò)中傳遞對象時,都會用到對象序列化。

Java序列化API為處理對象序列化提供了一個標(biāo)準(zhǔn)機制,該API簡單易用。

很多框架中都有用到,比如典型的dubbo框架中使用了序列化。

序列化有什么作用?

序列化機制允許將實現(xiàn)序列化的Java對象轉(zhuǎn)換位字節(jié)序列,這些字節(jié)序列可以保存在磁盤上,或通過網(wǎng)絡(luò)傳輸,以達(dá)到以后恢復(fù)成原來的對象。序列化機制使得對象可以脫離程序的運行而獨立存在。

序列化實現(xiàn)方式

Java語言中,常見實現(xiàn)序列化的方式有兩種:

  • 實現(xiàn)Serializable接口

  • 實現(xiàn)Externalizable接口

下面我們就來詳細(xì)的說說這兩種實現(xiàn)方式。

  • 實現(xiàn)Serializable接口

創(chuàng)建一個User類實現(xiàn)Serializable接口 ,實現(xiàn)序列化,大致步驟為:

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2. 對象實體類實現(xiàn)Serializable 標(biāo)記接口。

  3. 創(chuàng)建序列化輸出流對象ObjectOutputStream,該對象的創(chuàng)建依賴于其它輸出流對象,通常我們將對象序列化為文件存儲,所以這里用文件相關(guān)的輸出流對象  FileOutputStream。

  4. 通過ObjectOutputStream 的 writeObject()方法將對象序列化為文件。

  5. 關(guān)閉流。

以下就是code:

package com.tian.my_code.test.clone;          import java.io.FileOutputStream;     import java.io.IOException;     import java.io.ObjectOutputStream;     import java.io.Serializable;          public class User implements Serializable {         private int age;         private String name;              public User() {         }              public User(int age, String name) {             this.age = age;             this.name = name;         }         //set get省略         public static void main(String[] args) {             try {                 ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.txt"));                 User user=new User(22,"老田");                 objectOutputStream.writeObject(user);             } catch (IOException e) {                 e.printStackTrace();             }         }     }

創(chuàng)建一個User對象,然后把User對象保存的user.txt中了。

反序列化

大致有以下三個步驟:

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2. 創(chuàng)建輸入流對象ObjectOutputStream。同樣依賴于其它輸入流對象,這里是文件輸入流 FileInputStream。

  3. 通過 ObjectInputStream 的 readObject()方法,將文件中的對象讀取到內(nèi)存。

  4. 關(guān)閉流。

下面我們再進(jìn)行反序列化code:

package com.tian.my_code.test.clone;          import java.io.*;          public class SeriTest {         public static void main(String[] args) {             try {                 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.txt"));                 User user=(User) ois.readObject();                 System.out.println(user.getName());             } catch (Exception e) {                 e.printStackTrace();             }         }     }

運行這段代碼,輸出結(jié)果:

如何理解序列化

使用IDEA打開user.tst文件:

如何理解序列化

使用編輯器16機制查看

如何理解序列化

關(guān)于文件內(nèi)容咱們就不用太關(guān)心了,繼續(xù)說我們的重點。

序列化是把User對象存放到文件里了,然后反序列化就是讀取文件內(nèi)容并創(chuàng)建對象。

A端把對象User保存到文件user.txt中,B端就可以通過網(wǎng)絡(luò)或者其他方式讀取到這個文件,再進(jìn)行反序列化,獲得A端創(chuàng)建的User對象。

拓展

如果B端拿到的User屬性如果有變化呢?比如說:增加一個字段

private String address;

再次進(jìn)行反序列化就會報錯

如何理解序列化

添加serialVersionUID

package com.tian.my_code.test.clone;          import java.io.FileOutputStream;     import java.io.IOException;     import java.io.ObjectOutputStream;     import java.io.Serializable;          public class User implements Serializable{         private static final long serialVersionUID = 2012965743695714769L;         private int age;         private String name;              public User() {         }              public User(int age, String name) {             this.age = age;             this.name = name;         }              // set get   省略              public static void main(String[] args) {             try {                 ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.txt"));                 User user=new User(22,"老田");                 objectOutputStream.writeObject(user);             } catch (IOException e) {                 e.printStackTrace();             }         }     }

再次執(zhí)行反序列化,運行結(jié)果正常

如何理解序列化

然后我們再次加上字段和對應(yīng)的get/set方法

private String address;

再次執(zhí)行反序列化

如何理解序列化

反序列化成功。

如果可序列化類未顯式聲明 serialVersionUID,則序列化運行時將基于該類的各個方面計算該類的默認(rèn) serialVersionUID  值,如“Java(TM) 對象序列化規(guī)范”中所述。

不過,強烈建議 所有可序列化類都顯式聲明 serialVersionUID 值,原因是計算默認(rèn)的  serialVersionUID對類的詳細(xì)信息具有較高的敏感性,根據(jù)編譯器實現(xiàn)的不同可能千差萬別,這樣在反序列化過程中可能會導(dǎo)致意外的  InvalidClassException。

因此,為保證 serialVersionUID值跨不同 Java 編譯器實現(xiàn)的一致性,序列化類必須聲明一個明確的  serialVersionUID值。

強烈建議使用 private 修飾符顯示聲明 serialVersionUID(如果可能),原因是這種聲明僅應(yīng)用于直接聲明類 --  serialVersionUID字段作為繼承成員沒有用處。數(shù)組類不能聲明一個明確的  serialVersionUID,因此它們總是具有默認(rèn)的計算值,但是數(shù)組類沒有匹配 serialVersionUID值的要求。

所以,盡量顯示的聲明,這樣序列化的類即使有字段的修改,因為 serialVersionUID的存在,也能保證反序列化成功。保證了更好的兼容性。

IDEA中如何快捷添加serialVersionUID?

如何理解序列化

我們的類實現(xiàn)Serializable接口,鼠標(biāo)放在類上,Alt+Enter鍵就可以添加了。

實現(xiàn)Externalizable接口

通過實現(xiàn)Externalizable接口,必須實現(xiàn)writeExternal、readExternal方法。

如何理解序列化

如何理解序列化

@Override public void writeExternal(ObjectOutput out) throws IOException { } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {      }

Externalizable是Serializable的子接口。

public interface Externalizable extends java.io.Serializable {

繼續(xù)使用前面的User,代碼進(jìn)行改造:

package com.tian.my_code.test.clone;          import java.io.*;          public class User implements Externalizable {         private int age;         private String name;              public User() {         }              public User(int age, String name) {             this.age = age;             this.name = name;         }              //set get                   public static void main(String[] args) {             try {                 ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.txt"));                 User user = new User(22, "老田");                 objectOutputStream.writeObject(user);             } catch (IOException e) {                 e.printStackTrace();             }         }              @Override         public void writeExternal(ObjectOutput out) throws IOException {             //將name反轉(zhuǎn)后寫入二進(jìn)制流             StringBuffer reverse = new StringBuffer(name).reverse();             out.writeObject(reverse);             out.writeInt(age);         }              @Override         public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {             //將讀取的字符串反轉(zhuǎn)后賦值給name實例變量             this.name = ((StringBuffer) in.readObject()).reverse().toString();             //將讀取到的int類型值付給age             this.age = in.readInt();         }     }

執(zhí)行序列化,然后再次執(zhí)行反序列化,輸出:

如何理解序列化

注意

Externalizable接口不同于Serializable接口,實現(xiàn)此接口必須實現(xiàn)接口中的兩個方法實現(xiàn)自定義序列化,這是強制性的;特別之處是必須提供public的無參構(gòu)造器,因為在反序列化的時候需要反射創(chuàng)建對象。

兩種方式對比

下圖為兩種實現(xiàn)方式的對比:

如何理解序列化

序列化只有兩種方式嗎?

當(dāng)然不是。根據(jù)序列化的定義,不管通過什么方式,只要你能把內(nèi)存中的對象轉(zhuǎn)換成能存儲或傳輸?shù)姆绞?,又能反過來恢復(fù)它,其實都可以稱為序列化。因此,我們常用的Fastjson、Jackson等第三方類庫將對象轉(zhuǎn)成Json格式文件,也可以算是一種序列化,用JAXB實現(xiàn)XML格式文件輸出,也可以算是序列化。所以,千萬不要被思維局限,其實現(xiàn)實當(dāng)中我們進(jìn)行了很多序列化和反序列化的操作,涉及不同的形態(tài)、數(shù)據(jù)格式等。

序列化算法

  • 所有保存到磁盤的對象都有一個序列化編碼號。

  • 當(dāng)程序試圖序列化一個對象時,會先檢查此對象是否已經(jīng)序列化過,只有此對象從未(在此虛擬機)被序列化過,才會將此對象序列化為字節(jié)序列輸出。

  • 如果此對象已經(jīng)序列化過,則直接輸出編號即可。

自定義序列化

有些時候,我們有這樣的需求,某些屬性不需要序列化。使用transient關(guān)鍵字選擇不需要序列化的字段。

繼續(xù)使用前面的代碼進(jìn)行改造,在age字段上添加transient修飾:

 package com.tian.my_code.test.clone;          import java.io.FileOutputStream;     import java.io.IOException;     import java.io.ObjectOutputStream;     import java.io.Serializable;          public class User implements Serializable{         private transient int age;         private String name;                   public User() {         }              public User(int age, String name) {             this.age = age;             this.name = name;         }              public int getAge() {             return age;         }              public void setAge(int age) {             this.age = age;         }              public String getName() {             return name;         }              public void setName(String name) {             this.name = name;         }              public static void main(String[] args) {             try {                 ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.txt"));                 User user=new User(22,"老田");                 objectOutputStream.writeObject(user);             } catch (IOException e) {                 e.printStackTrace();             }         }     }     ```  序列化,然后進(jìn)行反序列化: ```java     package com.tian.my_code.test.clone;          import java.io.*;          public class SeriTest {         public static void main(String[] args) {             try {                 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.txt"));                 User user=(User) ois.readObject();                 System.out.println(user.getName());                 System.out.println(user.getAge());             } catch (Exception e) {                 e.printStackTrace();             }         }     }

運行輸出:

如何理解序列化

從輸出我們看到,使用transient修飾的屬性,Java序列化時,會忽略掉此字段,所以反序列化出的對象,被transient修飾的屬性是默認(rèn)值。

對于引用類型,值是null;基本類型,值是0;boolean類型,值是false。

探索

到此序列化內(nèi)容算講完了,但是,如果只停留在這個層面,是無法應(yīng)對實際工作中的問題的。

比如模型對象持有其它對象的引用怎么處理,引用類型如果是復(fù)雜些的集合類型怎么處理?

上面的User中持有String引用類型的,照樣序列化沒問題,那么如果是我們自定義的引用類呢?

比如下面的場景:

package com.tian.my_code.test.clone;          public class UserAddress {         private int provinceCode;         private int cityCode;              public UserAddress() {         }              public UserAddress(int provinceCode, int cityCode) {             this.provinceCode = provinceCode;             this.cityCode = cityCode;         }              public int getProvinceCode() {             return provinceCode;         }              public void setProvinceCode(int provinceCode) {             this.provinceCode = provinceCode;         }              public int getCityCode() {             return cityCode;         }              public void setCityCode(int cityCode) {             this.cityCode = cityCode;         }     }

然后在User中添加一個UserAddress的屬性:

package com.tian.my_code.test.clone;          import java.io.FileOutputStream;     import java.io.IOException;     import java.io.ObjectOutputStream;     import java.io.Serializable;          public class User implements Serializable{         private static final long serialVersionUID = -2445226500651941044L;         private int age;         private String name;         private UserAddress userAddress;              public User() {         }              public User(int age, String name) {             this.age = age;             this.name = name;         }         //get set                  public static void main(String[] args) {             try {                 ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.txt"));                 User user=new User(22,"老田");                 UserAddress userAddress=new UserAddress(10001,10001001);                 user.setUserAddress(userAddress);                 objectOutputStream.writeObject(user);             } catch (IOException e) {                 e.printStackTrace();             }         }     }

運行上面代碼:

如何理解序列化

拋出了 java.io.NotSerializableException  異常。很明顯在告訴我們,UserAddress沒有實現(xiàn)序列化接口。待UserAddress類實現(xiàn)序列化接口后:

  1. package com.tian.my_code.test.clone; 

  2.      

  3.     import java.io.Serializable; 

  4.      

  5.     public class UserAddress implements Serializable { 

  6.         private static final long serialVersionUID = 5128703296815173156L; 

  7.         private int provinceCode; 

  8.         private int cityCode; 

  9.      

  10.         public UserAddress() { 

  11.         } 

  12.      

  13.         public UserAddress(int provinceCode, int cityCode) { 

  14.             this.provinceCode = provinceCode; 

  15.             this.cityCode = cityCode; 

  16.         } 

  17.         //get set 

  18.     } 


再次運行,正常不報錯了。

反序列化代碼:

package com.tian.my_code.test.clone;          import java.io.*;          public class SeriTest {         public static void main(String[] args) {             try {                 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.txt"));                 User user=(User) ois.readObject();                 System.out.println(user.getName());                 System.out.println(user.getAge());                 System.out.println(user.getUserAddress().getProvinceCode());                 System.out.println(user.getUserAddress().getCityCode());             } catch (Exception e) {                 e.printStackTrace();             }         }     }

運行結(jié)果:

如何理解序列化

典型運用場景

public final class String implements java.io.Serializable, Comparable, CharSequence {        private static final long serialVersionUID = -6849794470754667710L;    }    public class HashMap extends AbstractMap  implements Map, Cloneable, Serializable {        private static final long serialVersionUID = 362498820763181265L;    }    public class ArrayList extends AbstractList  implements List, RandomAccess, Cloneable, java.io.Serializable{        private static final long serialVersionUID = 8683452581122892189L;    }    .....

很多常用類都實現(xiàn)了序列化接口。

再次拓展

上面說的transient  反序列化的時候是默認(rèn)值,但是你會發(fā)現(xiàn),幾種常用集合類ArrayList、HashMap、LinkedList等數(shù)據(jù)存儲字段,竟然都被 transient  修飾了,然而在實際操作中我們用集合類型存儲的數(shù)據(jù)卻可以被正常的序列化和反序列化?

如何理解序列化

真相當(dāng)然還是在源碼里。實際上,各個集合類型對于序列化和反序列化是有單獨的實現(xiàn)的,并沒有采用虛擬機默認(rèn)的方式。這里以  ArrayList中的序列化和反序列化源碼部分為例分析:

private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{             int expectedModCount = modCount;             //序列化當(dāng)前ArrayList中非transient以及非靜態(tài)字段             s.defaultWriteObject();             //序列化數(shù)組實際個數(shù)             s.writeInt(size);             // 逐個取出數(shù)組中的值進(jìn)行序列化             for (int i=0; i 0) {                 // 容量計算                 int capacity = calculateCapacity(elementData, size);                 SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);                 //檢測是否需要對數(shù)組擴(kuò)容操作                 ensureCapacityInternal(size);                 Object[] a = elementData;                 // 按順序反序列化數(shù)組中的值                 for (int i=0; i

讀源碼可以知道,ArrayList的序列化和反序列化主要思路就是根據(jù)集合中實際存儲的元素個數(shù)來進(jìn)行操作,這樣做估計是為了避免不必要的空間浪費(因為ArrayList的擴(kuò)容機制決定了,集合中實際存儲的元素個數(shù)肯定比集合的可容量要小)。為了驗證,我們可以在單元測試序列化和返序列化的時候,在ArrayLIst的兩個方法中打上斷點,以確認(rèn)這兩個方法在序列化和返序列化的執(zhí)行流程中(截圖為反序列化過程):

原來,我們之前自以為集合能成功序列化也只是簡單的實現(xiàn)了標(biāo)記接口都只是表象,表象背后有各個集合類有不同的深意。所以,同樣的思路,讀者朋友可以自己去分析下  HashMap以及其它集合類中自行控制序列化和反序列化的個中門道了,感興趣的小伙伴可以自行去查看一番。

序列化注意事項

1、序列化時,只對對象的狀態(tài)進(jìn)行保存,而不管對象的方法;

2、當(dāng)一個父類實現(xiàn)序列化,子類自動實現(xiàn)序列化,不需要顯式實現(xiàn)Serializable接口;

3、當(dāng)一個對象的實例變量引用其他對象,序列化該對象時也把引用對象進(jìn)行序列化;

4、并非所有的對象都可以序列化,至于為什么不可以,有很多原因了,比如:

安全方面的原因,比如一個對象擁有private,public等field,對于一個要傳輸?shù)膶ο?,比如寫到文件,或者進(jìn)行RMI傳輸?shù)鹊?,在序列化進(jìn)行傳輸?shù)倪^程中,這個對象的private等域是不受保護(hù)的;

資源分配方面的原因,比如socket,thread類,如果可以序列化,進(jìn)行傳輸或者保存,也無法對他們進(jìn)行重新的資源分配,而且,也是沒有必要這樣實現(xiàn);

5、聲明為static和transient類型的成員數(shù)據(jù)不能被序列化。因為static代表類的狀態(tài),transient代表對象的臨時數(shù)據(jù)。

6、序列化運行時使用一個稱為 serialVersionUID  的版本號與每個可序列化類相關(guān)聯(lián),該序列號在反序列化過程中用于驗證序列化對象的發(fā)送者和接收者是否為該對象加載了與序列化兼容的類。為它賦予明確的值。顯式地定義serialVersionUID有兩種用途:

  • 在某些場合,希望類的不同版本對序列化兼容,因此需要確保類的不同版本具有相同的serialVersionUID;

  • 在某些場合,不希望類的不同版本對序列化兼容,因此需要確保類的不同版本具有不同的serialVersionUID。

7、Java有很多基礎(chǔ)類已經(jīng)實現(xiàn)了serializable接口,比如String,Vector等。但是也有一些沒有實現(xiàn)serializable接口的;

8、如果一個對象的成員變量是一個對象,那么這個對象的數(shù)據(jù)成員也會被保存!這是能用序列化解決深拷貝的重要原因;

“如何理解序列化”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!


標(biāo)題名稱:如何理解序列化
地址分享:http://weahome.cn/article/jeccgd.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部