亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

Java對象序列化與反序列化

2019-11-14 22:16:49
字體:
來源:轉載
供稿:網友
java對象序列化與反序列化

對象序列化的目標是將對象保存在磁盤中或者在網絡中進行傳輸。實現的機制是允許將對象轉為與平臺無關的二進制流。

java中對象的序列化機制是將允許對象轉為字節序列。這些字節序列可以使Java對象脫離程序存在,從而可以保存在磁盤上,也可以在網絡間傳輸。

對象的序列化是將一個Java對象寫入IO流;與此對應的,反序列化則是從IO流中恢復一個Java對象。

實現序列化

如果要將一個java對象序列化,那么對象的類需要是可序列化的。要讓類可序列化,那么這個類需要實現如下兩個接口:

  • Serializable
  • Externalizable
使用Serializable序列化

實現Serializable接口非常簡單,只要讓java實現Serializable接口即可,無需實現任何方法。

一個類一旦實現了Serializable接口,那么該類的對象就是可序列化的。實現類的對象的序列化可以使用ObjectOutputStream,實現步驟如下:

  • 創建ObjectOutputStream對象;
  • 調用ObjectOutputStream的writeObject方法輸出對象。

以下是一個實例:

package com.zhyea.test;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectOutputStream;import java.io.Serializable;/** * 序列化測試類 *  * @author robin * @date 2014年12月18日 */public class SerialTest {    public static void main(String[] args) {        ObjectOutputStream oos = null;        try {            oos = new ObjectOutputStream(new FileOutputStream("D://object.txt"));            Person robin = new Person("robin", 29);            oos.writeObject(robin);        } catch (IOException e) {            e.PRintStackTrace();        } finally {            if (null != oos) {                try {                    oos.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }}/** * 序列化測試用對象 *  * @author robin * @date 2014年12月18日 */class Person implements Serializable{        private static final long serialVersionUID = -6412852654889352693L;        /**     * 姓名     */    private String name;    /**     * 年齡     */    private int age;    public Person() {    }    public Person(String name, int age) {        this.name = name;        this.age = age;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }}

如上的代碼實現了將一個Person對象保存在了磁盤的一個文本文件object.txt上。運行程序在D盤上生成了一個object.txt文件。以下是文件內容:

image

有亂碼(字節流轉字符流導致的),但仍不影響我們分辨出里面是不是我們保存的對象。

接下來需要反序列化將Person對象從磁盤上讀出。相應的反序列化需要使用的類是ObjectInputStream,反序列化步驟如下:

  • 創建ObjectInputStream對象;
  • 使用ObjectInputStream的readObject方法取出對象。

接下來,重構下我們的代碼,實現反序列化,如下:

package com.zhyea.test;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;/** * 序列化測試類 *  * @author robin * @date 2014年12月18日 */public class SerialTest {    public static void main(String[] args) {        Person robin = new Person("robin", 29);        String savePath = "D://object.txt";        SerialTest test = new SerialTest();        try {            test.serialize(robin, savePath);            Person person = (Person) test.deSerialize(savePath);            System.out.println("Name:" + person.getName() + "   Age:"                    + person.getAge());        } catch (IOException e) {            e.printStackTrace();        } catch (ClassNotFoundException e) {            e.printStackTrace();        }    }    /**     * 實現序列化     *      * @param obj     *            要被序列化保存的對象     * @param path     *            保存地址     * @throws IOException     */    public void serialize(Object obj, String path) throws IOException {        ObjectOutputStream oos = null;        try {            oos = new ObjectOutputStream(new FileOutputStream(path));            oos.writeObject(obj);        } finally {            if (null != oos)                oos.close();        }    }    /**     * 反序列化取出對象     *      * @param path     *            被序列化對象保存的位置     * @return     * @throws IOException     * @throws ClassNotFoundException     */    public Object deSerialize(String path) throws IOException,            ClassNotFoundException {        ObjectInputStream ois = null;        try {            ois = new ObjectInputStream(new FileInputStream(path));            return ois.readObject();        } finally {            if (null != ois)                ois.close();        }    }}/** * 序列化測試用對象 *  * @author robin * @date 2014年12月18日 */class Person implements Serializable {    private static final long serialVersionUID = -6412852654889352693L;    /**     * 姓名     */    private String name;    /**     * 年齡     */    private int age;    public Person() {    }    public Person(String name, int age) {        this.name = name;        this.age = age;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }}

關于對象序列化與反序列化還有幾點需要注意:

  • 反序列化無需通過構造器初始化對象;
  • 如果使用序列化機制向文件中寫入了多個對象,那么取出和寫入的順序必須一致;
  • Java對類的對象進行序列化時,若類中存在對象引用(且值不為null),也會對類的引用對象進行序列化。
使用transient

在一些特殊場景下,比如銀行賬戶對象,出于保密考慮,不希望對存款金額進行序列化?;蛘哳惖囊恍┮妙愋偷某蓡T是不可序列化的。此時可以使用transient關鍵字修飾不想被或者不能被序列化的成員變量。

繼續調整我們的代碼來做演示:

package com.zhyea.test;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;/** * 序列化測試類 *  * @author robin * @date 2014年12月18日 */public class SerialTest {    public static void main(String[] args) {        Person robin = new Person("robin", 29);        School school = new School("XX學校");                Teacher tRobin = new Teacher(robin);        tRobin.setSchool(school);        tRobin.setSalary(12.0);                String savePath = "D://object.txt";        SerialTest test = new SerialTest();        try {            test.serialize(savePath, tRobin);                        Teacher t = (Teacher) test.deSerialize(savePath);            System.out.println("Name:" + t.getPerson().getName()                             +" Salary:" + t.getSalary());        } catch (IOException e) {            e.printStackTrace();        } catch (ClassNotFoundException e) {            e.printStackTrace();        }    }    /**     * 實現序列化     *      * @param obj     *            要被序列化保存的對象     * @param path     *            保存地址     * @throws IOException     */    public void serialize(String path, Object ... obj) throws IOException {        ....    }    /**     * 反序列化取出對象     *      * @param path     *            被序列化對象保存的位置     * @return     * @throws IOException     * @throws ClassNotFoundException     */    public Object deSerialize(String path) throws IOException,            ClassNotFoundException {        ...    }}/** * Teacher類 * @author robin * @date 2014年12月18日 */class Teacher implements Serializable{        private static final long serialVersionUID = -8751853088437904443L;        private Person person;    private transient School school;    private transient double salary;        public Teacher(Person person){        this.person = person;    }        /*略去get、set,請自行補充*/}/** * School類,不可序列化 *  * @author robin * @date 2014年12月18日 */class School{    private String name;        public School(String name){        this.name = name;    }    /*略去get、set,請自行補充*/}/** * Person類,可序列化 *  * @author robin * @date 2014年12月18日 */class Person implements Serializable {     ....}

在不對Teacher類的school成員添加transient標識的情況下,若school值不為null,會報NotSerializableException。異常信息如下: image

在不對Teacher類的salary成員添加transient標識的時候,會如實輸出salary的值,添加后則只會輸出salary的默認初始值即0.0。

image

需要注意的是transient只能修飾屬性(filed),不能修飾類或方法。

自定義序列化

transient提供了一種簡潔的方式將被transient修飾的成員屬性完全隔離在序列化機制之外。這樣子固然不錯,但是Java還提供了一種自定義序列化機制讓開發者更自由地控制如何序列化各個成員屬性,或者不序列化某些屬性(與transient效果相同)。

在需要自定義序列化和反序列化的類中需要提供以下方法:

  • private void writeObject(ObjectOutputStream out)
  • private void readObject(ObjectInputStream in)
  • private void readObjectNoData()

先說下前兩個方法writeObject和readObject,這兩個方法和ObjectOutputStream及ObjectInputStream里對應的方法名稱相同。實際上,盡管這兩個方法是private型的,但是仍然是在被序列化(或反序列化)階段被外部類ObjectOutputStream(或ObjectInputStream)調用。僅以序列化為例,ObjectOutputStream在執行自己的writeObject方法前會先通過反射在要被序列化的對象的類中(有點繞口是吧)查找有無自定義的writeObject方法,如有的話,則會優先調用自定義的writeObject方法。因為查找反射方法時使用的是getPrivateMethod,所以自定以的writeObject方法的作用域要被設置為private。通過自定義writeObject和readObject方法可以完全控制對象的序列化與反序列化。

如下是示例代碼:

package com.zhyea.test;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;import com.sun.xml.internal.ws.encoding.soap.DeserializationException;/** * 序列化測試類 *  * @author robin * @date 2014年12月18日 */public class SerialTest {    public static void main(String[] args) {        Person robin = new Person("robin", 29);                String savePath = "D://object.txt";        SerialTest test = new SerialTest();        try {            test.serialize(savePath, robin);            Person person = (Person) test.deSerialize(savePath);                        System.out.println("Name:" + person.getName()                             +" Age:" + person.getAge());                    } catch (IOException e) {            e.printStackTrace();        } catch (ClassNotFoundException e) {            e.printStackTrace();        }     }    /**     * 實現序列化     *      * @param obj     *            要被序列化保存的對象     * @param path     *            保存地址     * @throws IOException     */    public void serialize(String path, Person ... obj) throws IOException {        ObjectOutputStream oos = null;        ...    }    /**     * 反序列化取出對象     *      * @param path     *            被序列化對象保存的位置     * @return     * @throws IOException     * @throws ClassNotFoundException     */    public Object deSerialize(String path) throws IOException,            ClassNotFoundException {        ...    }}/** * Person類,可序列化 *  * @author robin * @date 2014年12月18日 */class Person implements Serializable {    private static final long serialVersionUID = -6412852654889352693L;    /**     * 姓名     */    private String name;    /**     * 年齡     */    private int age;    public Person() {}    public Person(String name, int age) {        this.name = name;        this.age = age;    }    /*  略去get和set,請自行實現  */            private void writeObject(ObjectOutputStream out) throws IOException{        out.writeObject(name);        out.writeInt(age + 1);                System.out.println("my write");    }        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{        this.name = "zhangsan";        this.age = 30;        System.out.println("my read");    }}

以下是輸出結果:

image

關于readObjectNoData,在網上找了如下一段說明:

readObjectNoData   原始情況    pojo        public class Person implements Serializable {                           private int age;                            public Person() {  }                          //setter getter...              }    序列化         Person p = new Person();                      p.setAge(10);                      ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("c:/person.ser"));                      oos.writeObject(p);                     oos.flush();                      oos.close();  類結構變化后, 序列化數據不變     pojo      Animal        implements Serializable        顯式編寫readObjectNoData        public class Animal implements Serializable {                                  private String name;                                 public Animal() {  }                                 //setter getter...                                 private void readObjectNoData() {                                           this.name = "zhangsan";                                 }                  }      Person         extends Animal        public class Person extends Animal implements Serializable {                                  private int age;                                  public Person() {  }                                 // setter getter...                 }    反序列化      ObjectInputStream ois = new ObjectInputStream(new FileInputStream("c:/person.ser"));             Person sp = (Person) ois.readObject();             System.out.println(sp.getName());      readObject時, 會調用readObjectNoData

readObjectNoData在我理解看來像是一種異常處理機制,用來在序列化的流不完整的情況下返回正確的值。

使用 writeReplace和readResolve

writeReplace和readResolve是一種更徹底的序列化的機制,它甚至可以將序列化的目標對象替換為其它的對象。

但是與writeObject和readObject不同的是,這二者不是必須要一起使用的,而且盡量應分開使用。若一起使用的話,只有writeReplace會生效。

代碼可以說明一切,首先是writeReplace:

package com.zhyea.test;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.ObjectStreamException;import java.io.Serializable;/** * 序列化測試類 *  * @author robin * @date 2014年12月18日 */public class SerialTest {    public static void main(String[] args) {        Person robin = new Person("robin", 29);                String savePath = "D://object.txt";        SerialTest test = new SerialTest();        try {            //序列化            test.serialize(savePath, robin);            //反序列化            String person = (String) test.deSerialize(savePath);                        System.out.println(person);                    } catch (IOException e) {            e.printStackTrace();        } catch (ClassNotFoundException e) {            e.printStackTrace();        }     }    /**     * 實現序列化     *      * @param obj     *            要被序列化保存的對象     * @param path     *            保存地址     * @throws IOException     */    public void serialize(String path, Person ... obj) throws IOException {        ObjectOutputStream oos = null;        ....    }    /**     * 反序列化取出對象     *      * @param path     *            被序列化對象保存的位置     * @return     * @throws IOException     * @throws ClassNotFoundException     */    public Object deSerialize(String path) throws IOException,            ClassNotFoundException {        ....    }}/** * Person類,可序列化 *  * @author robin * @date 2014年12月18日 */class Person implements Serializable {    private static final long serialVersionUID = -6412852654889352693L;    /**     * 姓名     */    private String name;    /**     * 年齡     */    private int age;    public Person() {}    public Person(String name, int age) {        this.name = name;        this.age = age;    }    /*   set和get方法請自行添加    */            private Object writeReplace() throws ObjectStreamException{        System.out.println("my writeReplace");        return "robin";    }        private Object readResolve() throws ObjectStreamException{        System.out.println("my readResolve");        return "zhangsan";    }        private void writeObject(ObjectOutputStream out) throws IOException{        ....    }        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{        ....    }}

以下是運行結果:

image

在Person類中,保留了之前的writeObject和readObject方法,并且還添加了readResolve方法。但是從運行結果可以看出來,這3個方法都沒有被調用,只有writeReplace方法被使用??梢岳斫鉃楫斒褂脀riteReplace時,其他的自定義方法都不會被調用,即writeReplace的優先級最高。

現在注釋掉writeReplace方法,再次執行,結果如下:

image

這次writeObject,readObject和readResolve方法都被調用。readResolve方法緊跟著readObject方法被調用且最終返回的值是readResolve返回的值,readObject里反序列化生成的對象被拋棄。

此外還有一點需要說明:writeReplace和readResolve可以使用任何作用域,這意味著子類也可以調用超類的這兩個方法。但是如果子類還有不同的序列化及反序列化需求,這就需要子類重寫這個方法,有些時候這樣做是沒有必要的。因此一般情況下將這兩個方法的作用域設置為private。

使用Externalizable

一開始有提到過實現Externalizable接口也可以實現類的序列化。使用這種方法,可以由開發者完全決定如何序列化和反序列化目標對象。Externalizable接口提供了writeExternal和readExternal兩個方法。

實際上這種方法和前面的自定義序列化方法很相似,只是Externalizable強制自定義序列化。在使用了Externalizable的類中仍可以使用writeReplace和readResolve方法。使用Externalizable進行序列化較之使用Serializable性能略好,但是復雜度較高。

版本問題

執行序列化和反序列化時有可能會遇到JRE版本問題。尤其是在網絡的兩端進行通信時,這種情況更為多見。

為了解決這種問題,Java允許為序列化的類提供一個serialVersionUID的常量標識該類的版本。只要serialVersionUID的值不變,Java就會把它們當作相同的序列化版本。

如果不顯式定義serialVersionUID,那么JVM就會計算出一個serialVersionUID的值。不同的編譯器下會產生不同的serialVersionUID值。serialVersionUID值不同則會導致編譯失敗??梢允褂胘dk的bin目錄下的serial.exe查看可序列化類的serialVersionUID,指令如下:

serial Person

如果對類的修改確實會導致反序列化失敗,則應主動調整serialVersionUID的值。導致類的反序列化失敗的修改有以下幾種情形:

  • 只是修改了類的方法,不會影響反序列化。
  • 只是修改了類的static Field或transient Field,不會影響反序列化。
  • 修改了類的非static和非transient Field,會影響序列化。
序列化注意事項

關于對象的序列化,總結下注意事項:

  • 對象的類名、Field(包括基本類型、數組及對其他對象的引用)都會被序列化,對象的static Field,transient Field及方法不會被序列化;
  • 實現Serializable接口的類,如不想某個Field被序列化,可以使用transient關鍵字進行修飾;
  • 保證序列化對象的引用類型Filed的類也是可序列化的,如不可序列化,可以使用transient關鍵字進行修飾,否則會序列化失敗;
  • 反序列化時必須要有序列化對象的類的class文件;
  • 當通過文件網絡讀取序列化對象的時候,必需按寫入的順序來讀取。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美午夜性色大片在线观看| 超碰精品一区二区三区乱码| 亚洲一区二区自拍| 日韩视频中文字幕| 国产精品久久久久久久7电影| 色妞在线综合亚洲欧美| 国产成人福利视频| 日韩a**中文字幕| 欧美极品美女视频网站在线观看免费| 亚洲欧美激情视频| 伊人伊成久久人综合网站| 欧美激情手机在线视频| 国产乱肥老妇国产一区二| 亚洲性夜色噜噜噜7777| 久久久精品久久| 欧美在线亚洲一区| 国产精品白丝av嫩草影院| 久久久久久久久久久免费精品| 欧美亚洲激情在线| 国产综合久久久久| 久久99国产综合精品女同| 国产精品视频自在线| 狠狠躁夜夜躁人人躁婷婷91| 国产精品偷伦一区二区| 色综合久久中文字幕综合网小说| 成人午夜一级二级三级| 97超碰国产精品女人人人爽| 97人人爽人人喊人人模波多| 欧美黑人性视频| 国模视频一区二区三区| 国产精品色婷婷视频| 欧美日韩国产91| 欧美国产日韩二区| 久久精品中文字幕免费mv| 日韩中文在线视频| 91美女福利视频高清| 日韩免费观看在线观看| 欧美视频精品一区| 久久99青青精品免费观看| 国产91精品高潮白浆喷水| 一本一道久久a久久精品逆3p| 中文字幕在线视频日韩| 亚洲一区二区三区成人在线视频精品| 欧美黑人国产人伦爽爽爽| 久久久国产在线视频| www日韩欧美| 欧美激情区在线播放| 亚洲欧洲在线看| 亚洲高清免费观看高清完整版| 国产精品男人爽免费视频1| 国产在线播放不卡| 欧美国产第二页| 国产在线一区二区三区| 正在播放亚洲1区| 日韩av不卡电影| 久久精品夜夜夜夜夜久久| 91美女片黄在线观看游戏| 久久夜精品香蕉| 国产精品国产福利国产秒拍| 欧美在线影院在线视频| 国产一区二区黄| 在线成人中文字幕| 国产91精品视频在线观看| 欧美黑人性猛交| 欧美激情欧美激情| 亚洲精品动漫久久久久| 精品久久久久久久久久久| 国产欧美婷婷中文| 日韩成人小视频| 亚洲无亚洲人成网站77777| 欧美主播福利视频| 精品国内自产拍在线观看| 青青草国产精品一区二区| 国产视频自拍一区| 久久久久这里只有精品| 精品国产91久久久久久| 国产精品亚洲精品| 精品视频9999| 中文字幕精品视频| 丝袜亚洲另类欧美重口| 午夜免费在线观看精品视频| 日韩欧美在线国产| 亚洲自拍av在线| 国产精品一区二区av影院萌芽| 成人av.网址在线网站| 日韩精品日韩在线观看| 亚洲2020天天堂在线观看| 欧美一级淫片videoshd| 亚洲国产精品专区久久| 国产精品久久久av久久久| 在线日韩精品视频| 亚洲第一福利网站| 久久人人爽国产| 国产a∨精品一区二区三区不卡| 久久亚洲精品小早川怜子66| 欧美日韩精品在线播放| 欧美激情一区二区久久久| 欧美午夜精品久久久久久久| 亚洲精品成人久久久| 久久亚洲精品视频| 91av在线播放视频| 欧美精品一本久久男人的天堂| 色噜噜国产精品视频一区二区| 国产婷婷色综合av蜜臀av| 日本视频久久久| 国产精品91在线观看| 国产精品扒开腿做爽爽爽男男| 国自产精品手机在线观看视频| 伊人伊成久久人综合网小说| 国产精品jizz在线观看麻豆| 亚洲综合成人婷婷小说| 欧美丰满老妇厨房牲生活| 久久99久国产精品黄毛片入口| 国产精品嫩草影院久久久| 日韩免费高清在线观看| 久久亚洲精品成人| y97精品国产97久久久久久| www.日韩视频| 亚洲精品久久久久久久久久久久久| 亚洲精品不卡在线| 亚洲成人国产精品| 久久久国产精品视频| 国产精品视频男人的天堂| 欧美精品18videos性欧美| 国内精品美女av在线播放| 91中文精品字幕在线视频| 亚洲国产精品悠悠久久琪琪| 久久av中文字幕| 精品高清美女精品国产区| 91久久综合亚洲鲁鲁五月天| 欧美激情va永久在线播放| 欧美黑人巨大xxx极品| 欧美日韩亚洲激情| 亚洲少妇激情视频| 精品无人区太爽高潮在线播放| 国产成人精品av| 国产91在线高潮白浆在线观看| 欧美国产在线视频| 中日韩美女免费视频网址在线观看| 久久伊人精品天天| 亚洲香蕉成人av网站在线观看| 亚洲最大激情中文字幕| 中文字幕亚洲色图| 亚洲成人av在线| 成人精品在线视频| 欧美日韩国产丝袜美女| 国产香蕉一区二区三区在线视频| 久久久久久久影视| 欧美日韩激情视频8区| 国产91露脸中文字幕在线| 亚洲一区二区三区乱码aⅴ| 最近2019年中文视频免费在线观看| 91爱爱小视频k| 国产一区二中文字幕在线看| 38少妇精品导航| 国产在线a不卡| 欧美一区二区三区图| 狠狠色狠狠色综合日日小说| 欧美黑人国产人伦爽爽爽| 精品美女国产在线| 国产精品www色诱视频| 九九精品视频在线| 大伊人狠狠躁夜夜躁av一区| 国产亚洲免费的视频看|