自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

面試官:詳細(xì)說(shuō)說(shuō)你對(duì)序列化的理解

開(kāi)發(fā) 前端
在Java語(yǔ)言中,程序運(yùn)行的時(shí)候,會(huì)產(chǎn)生很多對(duì)象,而對(duì)象信息也只是在程序運(yùn)行的時(shí)候才在內(nèi)存中保持其狀態(tài),一旦程序停止,內(nèi)存釋放,對(duì)象也就不存在了。

 [[355611]]

本文主要內(nèi)容

 

背景

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

怎么能讓對(duì)象永久的保存下來(lái)呢?--------對(duì)象序列化 。

何為序列化和反序列化?

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

 

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

 

有哪些使用場(chǎng)景?

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

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

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

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

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

序列化有什么作用?

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

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

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

  • 實(shí)現(xiàn)Serializable接口
  • 實(shí)現(xiàn)Externalizable接口

下面我們就來(lái)詳細(xì)的說(shuō)說(shuō)這兩種實(shí)現(xiàn)方式。

  • 實(shí)現(xiàn)Serializable接口

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

  1. 對(duì)象實(shí)體類實(shí)現(xiàn)Serializable 標(biāo)記接口。
  2. 創(chuàng)建序列化輸出流對(duì)象ObjectOutputStream,該對(duì)象的創(chuàng)建依賴于其它輸出流對(duì)象,通常我們將對(duì)象序列化為文件存儲(chǔ),所以這里用文件相關(guān)的輸出流對(duì)象 FileOutputStream。
  3. 通過(guò)ObjectOutputStream 的 writeObject()方法將對(duì)象序列化為文件。
  4. 關(guān)閉流。

以下就是code:

  1. package com.tian.my_code.test.clone; 
  2.      
  3.     import java.io.FileOutputStream; 
  4.     import java.io.IOException; 
  5.     import java.io.ObjectOutputStream; 
  6.     import java.io.Serializable
  7.      
  8.     public class User implements Serializable { 
  9.         private int age; 
  10.         private String name
  11.      
  12.         public User() { 
  13.         } 
  14.      
  15.         public User(int age, String name) { 
  16.             this.age = age; 
  17.             this.name = name
  18.         } 
  19.         //set get省略 
  20.         public static void main(String[] args) { 
  21.             try { 
  22.                 ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.txt")); 
  23.                 User user=new User(22,"老田"); 
  24.                 objectOutputStream.writeObject(user); 
  25.             } catch (IOException e) { 
  26.                 e.printStackTrace(); 
  27.             } 
  28.         } 
  29.     } 

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

反序列化

大致有以下三個(gè)步驟:

  1. 創(chuàng)建輸入流對(duì)象ObjectOutputStream。同樣依賴于其它輸入流對(duì)象,這里是文件輸入流 FileInputStream。
  2. 通過(guò) ObjectInputStream 的 readObject()方法,將文件中的對(duì)象讀取到內(nèi)存。
  3. 關(guān)閉流。

下面我們?cè)龠M(jìn)行反序列化code:

  1. package com.tian.my_code.test.clone; 
  2.      
  3.     import java.io.*; 
  4.      
  5.     public class SeriTest { 
  6.         public static void main(String[] args) { 
  7.             try { 
  8.                 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.txt")); 
  9.                 User user=(User) ois.readObject(); 
  10.                 System.out.println(user.getName()); 
  11.             } catch (Exception e) { 
  12.                 e.printStackTrace(); 
  13.             } 
  14.         } 
  15.     } 

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

 

使用IDEA打開(kāi)user.tst文件:

 

使用編輯器16機(jī)制查看

 

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

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

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

拓展

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

  1. private String address; 

再次進(jìn)行反序列化就會(huì)報(bào)錯(cuò)

 

添加serialVersionUID

  1. package com.tian.my_code.test.clone; 
  2.      
  3.     import java.io.FileOutputStream; 
  4.     import java.io.IOException; 
  5.     import java.io.ObjectOutputStream; 
  6.     import java.io.Serializable
  7.      
  8.     public class User implements Serializable
  9.         private static final long serialVersionUID = 2012965743695714769L; 
  10.         private int age; 
  11.         private String name
  12.      
  13.         public User() { 
  14.         } 
  15.      
  16.         public User(int age, String name) { 
  17.             this.age = age; 
  18.             this.name = name
  19.         } 
  20.      
  21.         // set get   省略 
  22.      
  23.         public static void main(String[] args) { 
  24.             try { 
  25.                 ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.txt")); 
  26.                 User user=new User(22,"老田"); 
  27.                 objectOutputStream.writeObject(user); 
  28.             } catch (IOException e) { 
  29.                 e.printStackTrace(); 
  30.             } 
  31.         } 
  32.     } 

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

 

然后我們?cè)俅渭由献侄魏蛯?duì)應(yīng)的get/set方法

  1. private String address; 

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

 

反序列化成功。

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

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

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

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

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

IDEA中如何快捷添加serialVersionUID?

 

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

實(shí)現(xiàn)Externalizable接口

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

  1. @Override 
  2. public void writeExternal(ObjectOutput out) throws IOException { 
  3. @Override 
  4. public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { 
  5.      

Externalizable是Serializable的子接口。

public interface Externalizable extends java.io.Serializable {

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

  1. package com.tian.my_code.test.clone; 
  2.      
  3.     import java.io.*; 
  4.      
  5.     public class User implements Externalizable { 
  6.         private int age; 
  7.         private String name
  8.      
  9.         public User() { 
  10.         } 
  11.      
  12.         public User(int age, String name) { 
  13.             this.age = age; 
  14.             this.name = name
  15.         } 
  16.      
  17.         //set get 
  18.      
  19.      
  20.         public static void main(String[] args) { 
  21.             try { 
  22.                 ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.txt")); 
  23.                 User user = new User(22, "老田"); 
  24.                 objectOutputStream.writeObject(user); 
  25.             } catch (IOException e) { 
  26.                 e.printStackTrace(); 
  27.             } 
  28.         } 
  29.      
  30.         @Override 
  31.         public void writeExternal(ObjectOutput out) throws IOException { 
  32.             //將name反轉(zhuǎn)后寫(xiě)入二進(jìn)制流 
  33.             StringBuffer reverse = new StringBuffer(name).reverse(); 
  34.             out.writeObject(reverse); 
  35.             out.writeInt(age); 
  36.         } 
  37.      
  38.         @Override 
  39.         public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { 
  40.             //將讀取的字符串反轉(zhuǎn)后賦值給name實(shí)例變量 
  41.             this.name = ((StringBuffer) in.readObject()).reverse().toString(); 
  42.             //將讀取到的int類型值付給age 
  43.             this.age = in.readInt(); 
  44.         } 
  45.     } 
  46.      

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

 

注意

Externalizable接口不同于Serializable接口,實(shí)現(xiàn)此接口必須實(shí)現(xiàn)接口中的兩個(gè)方法實(shí)現(xiàn)自定義序列化,這是強(qiáng)制性的;特別之處是必須提供public的無(wú)參構(gòu)造器,因?yàn)樵诜葱蛄谢臅r(shí)候需要反射創(chuàng)建對(duì)象。

兩種方式對(duì)比

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

 

序列化只有兩種方式嗎?

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

序列化算法

  • 所有保存到磁盤的對(duì)象都有一個(gè)序列化編碼號(hào)。
  • 當(dāng)程序試圖序列化一個(gè)對(duì)象時(shí),會(huì)先檢查此對(duì)象是否已經(jīng)序列化過(guò),只有此對(duì)象從未(在此虛擬機(jī))被序列化過(guò),才會(huì)將此對(duì)象序列化為字節(jié)序列輸出。
  • 如果此對(duì)象已經(jīng)序列化過(guò),則直接輸出編號(hào)即可。

自定義序列化

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

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

  1.  package com.tian.my_code.test.clone; 
  2.      
  3.     import java.io.FileOutputStream; 
  4.     import java.io.IOException; 
  5.     import java.io.ObjectOutputStream; 
  6.     import java.io.Serializable
  7.      
  8.     public class User implements Serializable
  9.         private transient int age; 
  10.         private String name
  11.      
  12.      
  13.         public User() { 
  14.         } 
  15.      
  16.         public User(int age, String name) { 
  17.             this.age = age; 
  18.             this.name = name
  19.         } 
  20.      
  21.         public int getAge() { 
  22.             return age; 
  23.         } 
  24.      
  25.         public void setAge(int age) { 
  26.             this.age = age; 
  27.         } 
  28.      
  29.         public String getName() { 
  30.             return name
  31.         } 
  32.      
  33.         public void setName(String name) { 
  34.             this.name = name
  35.         } 
  36.      
  37.         public static void main(String[] args) { 
  38.             try { 
  39.                 ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.txt")); 
  40.                 User user=new User(22,"老田"); 
  41.                 objectOutputStream.writeObject(user); 
  42.             } catch (IOException e) { 
  43.                 e.printStackTrace(); 
  44.             } 
  45.         } 
  46.     } 
  47.     ``` 
  48.  
  49. 序列化,然后進(jìn)行反序列化: 
  50. ```java 
  51.     package com.tian.my_code.test.clone; 
  52.      
  53.     import java.io.*; 
  54.      
  55.     public class SeriTest { 
  56.         public static void main(String[] args) { 
  57.             try { 
  58.                 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.txt")); 
  59.                 User user=(User) ois.readObject(); 
  60.                 System.out.println(user.getName()); 
  61.                 System.out.println(user.getAge()); 
  62.             } catch (Exception e) { 
  63.                 e.printStackTrace(); 
  64.             } 
  65.         } 
  66.     } 

運(yùn)行輸出:

 

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

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

探索

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

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

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

比如下面的場(chǎng)景:

  1. package com.tian.my_code.test.clone; 
  2.      
  3.     public class UserAddress { 
  4.         private int provinceCode; 
  5.         private int cityCode; 
  6.      
  7.         public UserAddress() { 
  8.         } 
  9.      
  10.         public UserAddress(int provinceCode, int cityCode) { 
  11.             this.provinceCode = provinceCode; 
  12.             this.cityCode = cityCode; 
  13.         } 
  14.      
  15.         public int getProvinceCode() { 
  16.             return provinceCode; 
  17.         } 
  18.      
  19.         public void setProvinceCode(int provinceCode) { 
  20.             this.provinceCode = provinceCode; 
  21.         } 
  22.      
  23.         public int getCityCode() { 
  24.             return cityCode; 
  25.         } 
  26.      
  27.         public void setCityCode(int cityCode) { 
  28.             this.cityCode = cityCode; 
  29.         } 
  30.     } 

然后在User中添加一個(gè)UserAddress的屬性:

  1. package com.tian.my_code.test.clone; 
  2.      
  3.     import java.io.FileOutputStream; 
  4.     import java.io.IOException; 
  5.     import java.io.ObjectOutputStream; 
  6.     import java.io.Serializable
  7.      
  8.     public class User implements Serializable
  9.         private static final long serialVersionUID = -2445226500651941044L; 
  10.         private int age; 
  11.         private String name
  12.         private UserAddress userAddress; 
  13.      
  14.         public User() { 
  15.         } 
  16.      
  17.         public User(int age, String name) { 
  18.             this.age = age; 
  19.             this.name = name
  20.         } 
  21.         //get set 
  22.          
  23.         public static void main(String[] args) { 
  24.             try { 
  25.                 ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.txt")); 
  26.                 User user=new User(22,"老田"); 
  27.                 UserAddress userAddress=new UserAddress(10001,10001001); 
  28.                 user.setUserAddress(userAddress); 
  29.                 objectOutputStream.writeObject(user); 
  30.             } catch (IOException e) { 
  31.                 e.printStackTrace(); 
  32.             } 
  33.         } 
  34.     } 

運(yùn)行上面代碼:

 

拋出了 java.io.NotSerializableException 異常。很明顯在告訴我們,UserAddress沒(méi)有實(shí)現(xiàn)序列化接口。待UserAddress類實(shí)現(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.     } 

再次運(yùn)行,正常不報(bào)錯(cuò)了。

反序列化代碼:

  1. package com.tian.my_code.test.clone; 
  2.      
  3.     import java.io.*; 
  4.      
  5.     public class SeriTest { 
  6.         public static void main(String[] args) { 
  7.             try { 
  8.                 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.txt")); 
  9.                 User user=(User) ois.readObject(); 
  10.                 System.out.println(user.getName()); 
  11.                 System.out.println(user.getAge()); 
  12.                 System.out.println(user.getUserAddress().getProvinceCode()); 
  13.                 System.out.println(user.getUserAddress().getCityCode()); 
  14.             } catch (Exception e) { 
  15.                 e.printStackTrace(); 
  16.             } 
  17.         } 
  18.     } 

運(yùn)行結(jié)果:

 

典型運(yùn)用場(chǎng)景

  1. public final class String implements java.io.Serializable, Comparable<String>, CharSequence { 
  2.        private static final long serialVersionUID = -6849794470754667710L; 
  3.    } 
  4.    public class HashMap<K,V> extends AbstractMap<K,V>  implements Map<K,V>, Cloneable, Serializable { 
  5.        private static final long serialVersionUID = 362498820763181265L; 
  6.    } 
  7.    public class ArrayList<E> extends AbstractList<E>  implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  8.        private static final long serialVersionUID = 8683452581122892189L; 
  9.    } 
  10.    ..... 

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

再次拓展

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

 

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

  1. private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ 
  2.             int expectedModCount = modCount; 
  3.             //序列化當(dāng)前ArrayList中非transient以及非靜態(tài)字段 
  4.             s.defaultWriteObject(); 
  5.             //序列化數(shù)組實(shí)際個(gè)數(shù) 
  6.             s.writeInt(size); 
  7.             // 逐個(gè)取出數(shù)組中的值進(jìn)行序列化 
  8.             for (int i=0; i<size; i++) { 
  9.                 s.writeObject(elementData[i]); 
  10.             } 
  11.             //防止在并發(fā)的情況下對(duì)元素的修改 
  12.             if (modCount != expectedModCount) { 
  13.                 throw new ConcurrentModificationException(); 
  14.             } 
  15.         } 
  16.      
  17.         private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { 
  18.             elementData = EMPTY_ELEMENTDATA; 
  19.             // 反序列化非transient以及非靜態(tài)修飾的字段,其中包含序列化時(shí)的數(shù)組大小 size 
  20.             s.defaultReadObject(); 
  21.             // 忽略的操作 
  22.             s.readInt(); // ignored 
  23.             if (size > 0) { 
  24.                 // 容量計(jì)算 
  25.                 int capacity = calculateCapacity(elementData, size); 
  26.                 SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity); 
  27.                 //檢測(cè)是否需要對(duì)數(shù)組擴(kuò)容操作 
  28.                 ensureCapacityInternal(size); 
  29.                 Object[] a = elementData; 
  30.                 // 按順序反序列化數(shù)組中的值 
  31.                 for (int i=0; i<size; i++) { 
  32.                     a[i] = s.readObject(); 
  33.                 } 
  34.             } 
  35.         } 

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

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

序列化注意事項(xiàng)

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

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

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

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

安全方面的原因,比如一個(gè)對(duì)象擁有private,public等f(wàn)ield,對(duì)于一個(gè)要傳輸?shù)膶?duì)象,比如寫(xiě)到文件,或者進(jìn)行RMI傳輸?shù)鹊龋谛蛄谢M(jìn)行傳輸?shù)倪^(guò)程中,這個(gè)對(duì)象的private等域是不受保護(hù)的;

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

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

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

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

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

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

總結(jié)

什么是序列化?序列化Java中常用實(shí)現(xiàn)方式有哪些?兩種實(shí)現(xiàn)序列化方式的對(duì)比,序列化算法?如何自定義序列化?Java集合框架中序列化是如何實(shí)現(xiàn)的?

這幾個(gè)點(diǎn)如果沒(méi)有g(shù)et到,麻煩請(qǐng)?jiān)俅伍喿x,或者加我微信進(jìn)群里大家一起聊。

參考:

  1. cnblogs.com/9dragon/p/10901448.html oschina.net/translate/serialization-in-java 

本文轉(zhuǎn)載自微信公眾號(hào)「 Java后端技術(shù)全?!梗梢酝ㄟ^(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系 Java后端技術(shù)全棧公眾號(hào)。

 

責(zé)任編輯:武曉燕 來(lái)源: Java后端技術(shù)全棧
相關(guān)推薦

2022-02-21 17:24:18

序列化對(duì)象存儲(chǔ)

2021-11-25 10:18:42

RESTfulJava互聯(lián)網(wǎng)

2021-08-09 07:47:40

Git面試版本

2020-12-01 08:47:36

Java異常開(kāi)發(fā)

2020-06-12 15:50:56

options前端服務(wù)器

2022-11-08 11:26:13

Go逃逸代碼

2021-09-16 07:52:18

算法應(yīng)用場(chǎng)景

2019-05-10 10:50:04

Spring AOPJDK動(dòng)態(tài)代理CGLIB動(dòng)態(tài)代理

2021-11-09 14:08:45

DockerDockerfileJava

2021-05-28 11:18:50

MySQLbin logredo log

2021-11-09 08:51:13

模式命令面試

2021-11-05 07:47:56

代理模式對(duì)象

2021-11-03 14:10:28

工廠模式場(chǎng)景

2021-11-02 22:04:58

模式

2021-11-10 07:47:49

組合模式場(chǎng)景

2021-08-16 08:33:26

git

2021-09-26 10:57:16

集合操作場(chǎng)景

2021-09-27 06:50:04

樹(shù)非線性數(shù)據(jù)

2021-09-28 07:12:09

測(cè)試路徑

2021-09-06 10:51:27

TypeScriptJavaScript
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)