欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中的序列化(Serializable)和反序列化

 更新時(shí)間:2023年09月28日 10:20:27   作者:飛鷹279  
這篇文章主要介紹了Java中的序列化(Serializable)和反序列化,?JAVA序列化與反序列化就是JAVA對(duì)象與一串字節(jié)流之間的相互轉(zhuǎn)換,?我們?cè)诔绦蛑袆?chuàng)建的JAVA對(duì)象只存在于JVM中,需要的朋友可以參考下

JAVA序列化與反序列化

JAVA序列化與反序列化就是JAVA對(duì)象與一串字節(jié)流之間的相互轉(zhuǎn)換, 我們?cè)诔绦蛑袆?chuàng)建的JAVA對(duì)象只存在于JVM中, 當(dāng)程序退出時(shí), 這些對(duì)象也就消失了, 而序列化正是為了將這些對(duì)象保存起來(lái)以僅將來(lái)使用,也可以將已經(jīng)序列化的對(duì)象傳送給其他JVM來(lái)使用,這些序列化的字節(jié)流是于JVM無(wú)關(guān)的, 也就是說(shuō)一個(gè)JVM序列化的對(duì)象可以在另一個(gè)JVM中反序列化

使用JAVA提供的序列化機(jī)制有以下兩條需要遵守的條件:

  • 該類必須直接實(shí)現(xiàn)java.io.Serializable接口或者間接從其繼承樹中實(shí)現(xiàn)該接口(也就是他的某個(gè)父類實(shí)現(xiàn)了這個(gè)接口);
  • 對(duì)于該類的所有無(wú)法序列化的屬性(本文指字段field, 而不是嚴(yán)格意義上的屬性property, 下同)必須使用transient修飾.

對(duì)以上兩個(gè)條件的補(bǔ)充說(shuō)明:

  1. 從其繼承樹中實(shí)現(xiàn)Serializable接口指的是該類的某個(gè)父類實(shí)現(xiàn)了這個(gè)接口, 要注意的是Object類并沒(méi)有實(shí)現(xiàn)該接口, 也就是說(shuō)默認(rèn)的情況下我們定義的類是不支持序列化的, 而JDK提供的某些類如String, 數(shù)組等實(shí)現(xiàn)了該接口;
  2. 無(wú)法序列化的屬性包括兩種:一種是主觀上不想保存的屬性, 如動(dòng)態(tài)生成的屬性或者考慮到性能上的要求不準(zhǔn)備保存的屬性; 另一種是由于該屬性的類型沒(méi)有實(shí)現(xiàn)序列化而無(wú)法保存的屬性, 如Thread類型的屬性;
  3. 序列化機(jī)制并不要求該類具有一個(gè)無(wú)參的構(gòu)造方法, 因?yàn)樵诜葱蛄谢倪^(guò)程中實(shí)際上是去其繼承樹上找到一個(gè)沒(méi)有實(shí)現(xiàn)Serializable接口的父類(最終會(huì)找到Object), 然后構(gòu)造該類的對(duì)象, 再逐層往下的去設(shè)置各個(gè)可以反序列化的屬性(也就是沒(méi)有被transient修飾的非靜態(tài)屬性).

在使用JDK提供的序列化機(jī)制時(shí)需要借助一對(duì)I/O流, ObjectOutputStream和ObjectInputStream, 這兩個(gè)流分別是進(jìn)行序列化和反序列化操作, 通過(guò)ObjectOutputStream類的writeObject(obj)方法可以將對(duì)象寫入到輸出流中, 通過(guò)ObjectInputStream類的readObject()方法可以從該輸入流中反序列化該對(duì)象出來(lái). 如下面的例子就是將Student類的一個(gè)對(duì)象序列化保存到文件中, 并從該文件反序列化重新構(gòu)建出該對(duì)象(僅僅為了展示如何使用,對(duì)異常及流的處理末加以注意):

   class Student implements Serializable{
      private String name;
      public Student(String name){
         this.name = name;
      }
      public String getName(){
         return name;
      }
   }
   class StudentSerializer{
      public static void main(String[] args) throws Exception{
         // create a Student object
         Student st = new Student("jason");
        // serialize the st to jason.se file
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("jason.se"));
        oos.writeObject(st);
        oos.close();
        // deserialize the object from jason.se
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("jason.se"));
        Student jason = (Student) ois.readObject();
        ois.close();
       // verify the name field of jason object
       assert "jason".equals(jason.getName());
      }
   }

從上例可以看出, JDK提供的序列化機(jī)制使用起來(lái)相當(dāng)簡(jiǎn)單, 要注意的是:

  1. 在反序列的JVM上必須能夠找到該類(有可能序列化和反序列化并不是在同一個(gè)JVM上進(jìn)行的), 否則就會(huì)拋出ClassNotFoundException;
  2. 由于ObjectInputStream.readObject()方法可以反序列化任何類的對(duì)象, 所以其返回類型為Object, 我們需要將其強(qiáng)轉(zhuǎn)成具體的類;
  3. 如何對(duì)不滿足序列化機(jī)制的兩個(gè)要求的類進(jìn)行序列化, 則會(huì)拋出NotSerializableException;
  4. 如果JVM發(fā)現(xiàn)序列化與反序列化的類文件"不相同", 則會(huì)拋出InvalidClassException.

JVM如何判斷序列化與反序列化的類文件是否相同呢? 并不是說(shuō)兩個(gè)類文件要完全一樣, 而是通過(guò)類的一個(gè)私有屬性serialVersionUID來(lái)判斷的, 如果我們沒(méi)有顯示的指定這個(gè)屬性, 那么JVM會(huì)自動(dòng)使用該類的hashcode值來(lái)設(shè)置這個(gè)屬性, 這個(gè)時(shí)候如果我們對(duì)類進(jìn)行改變(比如說(shuō)加一個(gè)屬性或者刪掉一個(gè)屬性)就會(huì)導(dǎo)致serialVersionUID不同, 所以對(duì)于準(zhǔn)備序列化的類, 一般情況下我們都會(huì)顯示的設(shè)置這個(gè)屬性, 這樣及時(shí)以后我們對(duì)該類進(jìn)行了某些改動(dòng), 只要這個(gè)值保持一樣, JVM就還是會(huì)認(rèn)為這個(gè)類文件是沒(méi)有變的, 需要注意的是這種改變不包括繼承結(jié)構(gòu)的變化. 該屬性必須以下面的修飾方法來(lái)設(shè)置:

//當(dāng)然這個(gè)值可以自己指定, 也可以通過(guò)JDK提供的serializer來(lái)查看其默認(rèn)的hashcode值.
    private static final long serialVersionUID = -4333316296251054416L;  

由于JDK提供的這種默認(rèn)的序列化機(jī)制是簡(jiǎn)單的將對(duì)象變成字節(jié)流, 有時(shí)候并不滿足我們的要求, 比如考慮到加密, 或者在反序列化完了后需要調(diào)用某個(gè)方法來(lái)初始化transient的屬性等等, JDK提供了一種擴(kuò)展的方法來(lái)增加對(duì)序列化和反序列化的控制. 那就是可以讓序列化的對(duì)象實(shí)現(xiàn)下面兩個(gè)固定的方法(注意修飾符和結(jié)構(gòu)是固定的, throws的exception可以變通, 比如直接寫成throws Exception也是可以的):

private void writeObject(ObjectOutputStream oos) throws IOException {}
   private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException{}

JVM在序列化和反序列化的過(guò)程中如果發(fā)現(xiàn)我們的類實(shí)現(xiàn)了這兩個(gè)方法, 就會(huì)在writeObject(obj)和readObject()的時(shí)候?qū)⒖刂屏鬓D(zhuǎn)交給這兩個(gè)方法, 這樣我們就可以來(lái)執(zhí)行一些額外的操作, 同時(shí)可以在這兩個(gè)方法中調(diào)用ObjectOutputStream的defaultWriteObject()和ObjectInputStream的defaultReadObject()來(lái)讓JVM幫我們來(lái)執(zhí)行底部的具體序列化和反序列化操作, 如下例所示:

   class Student implements Serializable{
      private String name;
      public Student(String name){
         this.name = name;
      }
      public String getName(){
        return name;
      }
      private void writeObject(ObjectOutputStream oos) throws IOException {
         oos.defaultWriteObject();
         // 可以執(zhí)行其他的操作, 比如對(duì)反列化的文件進(jìn)行加密等等
      }
      private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
         ois.defaultReadObject();
         //可以調(diào)用其他方法來(lái)進(jìn)行額外的初始化操作
      }
   }

注意此時(shí)僅僅是對(duì)需要序列化的類增加了這兩個(gè)私有方法, 而對(duì)如何將其序列化和反序列化上并沒(méi)有任何改變, 也就是說(shuō)我們的StudentSerializer類并不需要做任何改變, JVM的序列化機(jī)制會(huì)自行來(lái)控制, 當(dāng)然如果我們?cè)趦蓚€(gè)私有的writeObject()和readObject()中并沒(méi)有調(diào)回ObjectOutputStream的defaultWriteObject()和ObjectInputStream的defaultReadObject()方法的話, 那就并沒(méi)有序列化了(除非你自己實(shí)現(xiàn)這個(gè)序列化和反序列化的過(guò)程)

到此這篇關(guān)于Java中的序列化(Serializable)和反序列化的文章就介紹到這了,更多相關(guān)Java序列化與反序列化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論