溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java怎么用代碼實現序列化與反序列化?

發(fā)布時間:2020-05-22 11:42:37 來源:億速云 閱讀:345 作者:Leah 欄目:編程語言

Java怎么用代碼實現序列化與反序列化?針對這個問題,今天小編總結這篇有關序列化使用的文章,希望能幫助更多想解決這個問題的朋友找到更加簡單易行的辦法。

性質:接口類

package java.io

public interface Serializable

1.1 翻譯文檔

Serializability of a class is enabled by the class implementing the java.io.Serializable interface.

通過實現java.io.Serializable interface接口來序列化一個類。

Classes that do not implement this interface will not have any of their state serialized or deserialized.

沒有實現此接口的類任何狀態(tài)都不會序列化或反序列化。

All subtypes of a serializable class are themselves serializable.

可序列化類的所有子類而本身都是可序列化的。

The serialization interface has no methods or fields and serves only to identify the semantics of being serializable.

序列化接口沒有方法或字段域,它僅用來標識可序列化的語義。

(1)To allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype's public, protected, and (if accessible) package fields.

(2)The subtype may assume this responsibility only if the class it extends has an accessible no-arg constructor to initialize the class's state.  It is an error to declare a class Serializable if this is not the case.  The error will be detected at runtime.

(3)During deserialization, the fields of non-serializable classes will be initialized using the public or protected no-arg constructor of the class.  A no-arg constructor must be accessible to the subclass that is serializable.  The fields of serializable subclasses will be restored from the stream.

(1)為了讓非序列化類的子類可以被序列化,這個子類可以承擔保存和恢復超類的pulic,protected,package字段(如果可訪問的話)。

(2)只有當它拓展的類具有可訪問無參構造函數來初始化類的狀態(tài)時,子類才可以承擔這樣的責任。如果不是這種情況,就不能聲明一個類是可序列化的。這個錯誤將在運行的時候被檢測出來。

(3)在反序列化期間,非序列化類的字段將通過類的以public或者protected修飾的空參構造函數實例化。無參數構造函數必須可訪問可序列化的子類。序列化子類的字段能夠從字符流里被還原。

1.2 輔助理解

(1)(2)(3)三塊主要說了三件事:

  • 非序列化的父類,其子類實現序列化時承擔保存和恢復父類public、protected、package等子類可訪問到子類的字段;
  • 非序列化的父類,其子類進行序列化時,父類需要有用public或者protected修飾的空參構造函數;
  • 若無空參構造函數的父類,其子類在運行序列化時將正常進行,但反序列化時會發(fā)生錯誤,并拋出異常。但父類有空參構造函數,子類完成序列化,父類屬性卻沒有參與到序列化中。

1.3 注意:此處有三個坑。

  • (1)中所述父類未實現序列化,實現序列化的子類會承擔保存和恢復父類的public、protected、package等子類可訪問到子類的字段。此處我個人理解為實現序列化的子類進行序列化的時候繼承了未實現序列化的父類中子類可訪問到的屬性,但序列化時無法記錄下父類對象的狀態(tài)信息;
  • 此處文檔若要正確讀取理解,切記(1)(2)(3)不可拆分,要放在一起去理解(上文之所以分開是便于翻譯);
  • 此處英文翻譯成漢字,難以理解其真實含義,所以通過下面的代碼驗證來輔助理解

1.4 代碼驗證

輔以A/B兩套類型代碼對比理解:

1)A套

父類:Biology 類

package com.springboot.SpringBootDemo.serializable;

            public class Biology {

                public String type;

                private int num;

                public Biology(String type, int num) {
                    this.type = type;
                    this.num = num;
                }

                public String getType() {
                    return type;
                }

                public void setType(String type) {
                    this.type = type;
                }

                public int getNum() {
                    return num;
                }

                public void setNum(int num) {
                    this.num = num;
                }
            }

子類:People 類

package com.springboot.SpringBootDemo.serializable;

            import java.io.Serializable;

            public class People extends Biology implements Serializable{

                private static final long serialVersionUID = -6623611040000763479L;

                public String name;

                protected String gender;

                private int age;

                public People(String type, int num, String name ,String gender ,int age) {
                    super(type, num);
                    this.name = name;
                    this.gender = gender;
                    this.age = age;
                }

                public String getName() {
                    return name;
                }

                public void setName(String name) {
                    this.name = name;
                }

                public String getGender() {
                    return gender;
                }

                public void setGender(String gender) {
                    this.gender = gender;
                }

                public int getAge() {
                    return age;
                }

                public void setAge(int age) {
                    this.age = age;
                }
            }

測試類:

           import java.io.FileInputStream;
            import java.io.FileOutputStream;
            import java.io.IOException;
            import java.io.ObjectInputStream;
            import java.io.ObjectOutputStream;

            public class Test {

                public static void main(String[] args) throws IOException, ClassNotFoundException {
                    People pp = new People("human",10000,"張三","男",25);

                    FileOutputStream fos = new FileOutputStream("test.txt");
                    ObjectOutputStream oos = new ObjectOutputStream(fos);
                    oos.writeObject(pp);
                    oos.flush();
                    oos.close();

                    //反序列化
                    FileInputStream sfis = new FileInputStream("test.txt");
                    ObjectInputStream sois = new ObjectInputStream(sfis);
                    People p = (People) sois.readObject();
                    System.out.println(
                            p.getType() +" "+
                            p.getNum() +" "+
                            p.getName() +" "+
                            p.getGender() +" "+
                            p.getAge()
                            );
                }
            }

結果:

   Exception in thread "main" java.io.InvalidClassException: com.springboot.SpringBootDemo.serializable.People; no valid constructor
                at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(Unknown Source)
                at java.io.ObjectStreamClass.checkDeserialize(Unknown Source)
                at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
                at java.io.ObjectInputStream.readObject0(Unknown Source)
                at java.io.ObjectInputStream.readObject(Unknown Source)
                at com.springboot.SpringBootDemo.serializable.Test.main(Test.java:23)

結果說明:在序列化時未發(fā)生異常,而在反序列化readObject()時發(fā)生異常。也就是說,父類沒有無參構造函數時,序列化正常進行,但反序列化時拋出newInvalidClassException異常。

2)B套

父類:Person類

            public class Person {

                public String name;

                public String gender;

                public int age;

                float height;

                public String getName() {
                    return name;
                }

                public void setName(String name) {
                    this.name = name;
                }

                public String getGender() {
                    return gender;
                }

                public void setGender(String gender) {
                    this.gender = gender;
                }

                public int getAge() {
                    return age;
                }

                public void setAge(int age) {
                    this.age = age;
                }

                public float getHeight() {
                    return height;
                }

                public void setHeight(float height) {
                    this.height = height;
                }
            }

子類:Male類

        import java.io.Serializable;

        public class Male extends Person implements Serializable{
            /**
             * 
             */
            private static final long serialVersionUID = -7361904256653535728L;

            public boolean beard;

            protected String weight;

            public boolean havaBeard(int age){
                boolean flag = false;

                if(age>=18){
                    flag = true;
                }
                return flag;
            }

            public boolean isBeard() {
                return beard;
            }

            public void setBeard(boolean beard) {
                this.beard = beard;
            }

            public String getWeight() {
                return weight;
            }

            public void setWeight(String weight) {
                this.weight = weight;
            }

        }

測試類:

        import java.io.FileInputStream;
        import java.io.FileOutputStream;
        import java.io.IOException;
        import java.io.ObjectInputStream;
        import java.io.ObjectOutputStream;

        public class SubTypeSerializable {

            public static void main(String[] args) throws IOException, ClassNotFoundException{

                /**Male繼承父類Person,自身實現序列化接口,其父類Person沒有實現序列化接口*/
                FileOutputStream fos = new FileOutputStream("male.txt");
                ObjectOutputStream oos = new ObjectOutputStream(fos);
                Male male = new Male();
                /**
                 * 其父類的父類Person的屬性
                 * 
                 * public String name;
                   public String gender;
                   public int age;
                   float height;
                 * */
                male.setName("張三");
                male.setGender("男性");
                male.setAge(25);
                male.setHeight(175);
                /**
                 * 其自身屬性
                 * public boolean beard;
                 * */
                male.setBeard(true);
                oos.writeObject(male);
                oos.flush();
                oos.close();

                //反序列化
                FileInputStream fis = new FileInputStream("male.txt");
                ObjectInputStream ois = new ObjectInputStream(fis);
                Male ml = (Male) ois.readObject();
                System.out.println(ml.getName() +" "+ml.getGender()+" "+ml.getHeight() +" "+ml.getAge()+" "+male.isBeard());
            }
    }          

結果:

    ml.getName() == null 
    ml.getGender() == null 
    ml.getHeight() == 0.0 
    ml.getAge() == 0 
    male.isBeard() == true

1.5 測試分析

1)父類屬性

public String name;

public String gender;

public int age;

float height;

其狀態(tài)信息均未被記錄;

2)自身屬性

public boolean beard;

其狀態(tài)信息被記錄

2.1 翻譯文檔

When traversing a graph, an object may be encountered that does not support the Serializable interface. In this case the NotSerializableException will be thrown and will identify the class of the non-serializable object.

當循環(huán)遍歷一個數據結構圖(數據結構圖可理解為數據結構類型,比如二叉樹)的時候,對象可能會遭遇到不支持實現序列化接口的情景。在這種情況下,將發(fā)生拋出NotSerializableException異常,并且該類被定義為不可序列化類。

Classes that require special handling during the serialization and deserialization process must implement special methods with these exact signatures:

在實現序列化和反序列化過程中,特殊處理的類需要實現這些特殊的方法:

```<PRE>
private void writeObject(java.io.ObjectOutputStream out) throws IOException

private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;

private void readObjectNoData() throws ObjectStreamException;

>The writeObject method is responsible for writing the state of the object for its particular class so that the corresponding readObject method can restore it.  The default mechanism for saving the Object's fields can be invoked by calling out.defaultWriteObject. The method does not need to concern itself with the state belonging to its superclasses or subclasses. State is saved by writing the individual fields to the ObjectOutputStream using the writeObject method or by using the methods for primitive data types supported by DataOutput.

witeObject方法負責寫入特定類的Object對象狀態(tài)信息,readObject方法可以還原該Object對象的狀態(tài)信息。保存該Object對象字段的默認機制是通過調用out.defaultWriteObject來實現。該方法不需要關注屬于其超類或子類的狀態(tài)。通過使用writeObject方法將各個字段寫入ObjectOutputStream,或使用DataOutput支持的基本數據類型的方法來保存狀態(tài)。

>The readObject method is responsible for reading from the stream and restoring the classes fields. It may call in.defaultReadObject to invoke the default mechanism for restoring the object's non-static and non-transient fields.  The defaultReadObject method uses information in the stream to assign the fields of the object saved in the stream with the correspondingly named fields in the current object.  This handles the case  when the class has evolved to add new fields. The method does not need to  concern itself with the state belonging to its superclasses or subclasses. State is saved by writing the individual fields to the ObjectOutputStream using the writeObject method or by using the methods for primitive data types supported by DataOutput.

readObject方法是負責讀取數據流并恢復該類的字段。它可以通過調用in.defaultReadObject來恢復非static以及非transient修飾的字段。defaultReadObject方法通過數據流中的信息,把當前類保存在數據流中的字段信息分配到相對應的字段名(也就是說把字段的值分配給相對應的字段名)。這種處理方式也能處理該類新增字段的情況。該方法不需要關注屬于其超類或子類的狀態(tài)。通過使用writeObject方法將各個字段寫入ObjectOutputStream,或使用DataOutput支持的基本數據類型的方法來保存狀態(tài)。通過writeObject方法把對象Object的各個字段寫入到ObjectOutputStream中,或者通過使用DataOutput支持的基本數據類型的方法來保存該Object對象的狀態(tài)信息。

>The readObjectNoData method is responsible for initializing the state of the object for its particular class in the event that the serialization stream does not list the given class as a superclass of the object being deserialized.  This may occur in cases where the receiving party uses a different version of the deserialized instance's class than the sending party, and the receiver's version extends classes that are not extended by the sender's version.  This may also occur if the serialization stream has been tampered; hence, readObjectNoData is useful for initializing deserialized objects properly despite a "hostile" or incomplete source stream.

(該處翻譯有些吃力,所以直接軟件翻譯,會后續(xù)進行代碼驗證體悟)

當出現反序列化與序列化類的版本不一致的情況時,readObjectNoData()標簽方法負責初始化對象的字段值。這種情況可能發(fā)生在反序列化時,接收方使用了發(fā)送方對象的類的不同版本,或者接收方繼承的類的版本與發(fā)送方繼承的類的版本不一致。另外,當序列化流被篡改了,也會發(fā)生這種情況。因此,當出現類不一致或者反序列化流不完全的情況時,readObjectNoData初始化反序列化對象的字段就非常有用了。

### 2.2 代碼驗證

#### 1)改變之前
public class Cat implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = -5731096200028489933L;

    public String color;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}
改變之前測試類:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class CatFamilylTest {

    public static void main(String[] args) throws Exception {
        serializable();
        deSerializable();
    }

    public static void serializable() throws Exception{
        Cat cat = new Cat();
        cat.setColor("white");
        FileOutputStream fos = new FileOutputStream("catFamily.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(cat);
        oos.flush();
        oos.close();
    }

    public static void deSerializable() throws Exception{
        FileInputStream sfis = new FileInputStream("catFamily.txt");
        ObjectInputStream sois = new ObjectInputStream(sfis);
        Cat cat = (Cat) sois.readObject();
        System.out.println(cat.getColor());
    }
}
結果:white

#### 2)第一次改變

第一次改變之增加父類:
import java.io.Serializable;

public class CatFamily implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = -7796480232179180594L;
    public String catType;

    public String getCatType() {
        return catType;
    }

    public void setCatType(String catType) {
        this.catType = catType;
    }

    private void readObjectNoData() {
        this.catType = "tiger";                 
    } 
}
第一次改變之后之Cat類變化:
public class Cat extends CatFamily{
    /**
     * 
     */
    private static final long serialVersionUID = -5731096200028489933L;

    public String color;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}
第一次改變之讀取已經存在的catFamily.txt文件:
public class CatFamilylTest {

    public static void main(String[] args) throws Exception {
        deSerializable();
    }
    public static void deSerializable() throws Exception{
        FileInputStream sfis = new FileInputStream("catFamily.txt");
        ObjectInputStream sois = new ObjectInputStream(sfis);
        Cat cat = (Cat) sois.readObject();
        System.out.println(cat.getColor()+" <---->"+cat.getCatType());
    }
}
第一次改變之結果:white <---->tiger

#### 3)第二次改變測試

第二次改變之父類:
public class CatFamily{

    public String catType;

    public String getCatType() {
        return catType;
    }

    public void setCatType(String catType) {
        this.catType = catType;
    }

    private void readObjectNoData() {
        this.catType = "tiger";                 
    } 
}
第二次改變之Cat類:
import java.io.Serializable;

public class Cat extends CatFamily implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = -5731096200028489933L;

    public String color;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}
第二次改變之測試類:
public class CatFamilylTest {

    public static void main(String[] args) throws Exception {
        deSerializable();

    }
    public static void deSerializable() throws Exception{
        FileInputStream sfis = new FileInputStream("catFamily.txt");
        ObjectInputStream sois = new ObjectInputStream(sfis);
        Cat cat = (Cat) sois.readObject();
        System.out.println(cat.getColor()+" <---->"+cat.getCatType());
    }
}
第二次改變之結果:white <---->null

#### 4)第三次改變舉例對比驗證

第三次改變之拋棄父類,且Cat類改變:
import java.io.Serializable;

public class Cat implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = -5731096200028489933L;

    public String type;

    public String color;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    private void readObjectNoData() {
        this.type = "hellokitty";                 
    }
}
第三次改變之測試類:
public class CatFamilylTest {

    public static void main(String[] args) throws Exception {
        deSerializable();
    }

    public static void deSerializable() throws Exception{
        FileInputStream sfis = new FileInputStream("catFamily.txt");
        ObjectInputStream sois = new ObjectInputStream(sfis);
        Cat cat = (Cat) sois.readObject();
        System.out.println(cat.getColor()+" <---->"+cat.getType());
    }
}
第三次改變之測試結果:white <---->null

### 2.3 測試代碼描述

#### 1)第一種(改變之前)

描述:建立實現序列化接口的Cat類,以及對應的測試類生產文件catFamily.txt

兩個目的:

- 用于建立變化的基礎層代碼;
- 生成序列化后的文件。

結果:反序列化catFamily.txt文件,得出正常結果 write 。

#### 2)第二種(第一次改變對比未改變)

改變之處:

- 增加實現序列化接口的父類CatFamily類,增添readObjectNoData()方法,并且設置屬性字段catType值為tiger;
- Cat類不直接實現序列化Serializable接口,而是繼承CatFamily類;
- 測試類對catFamily.txt進行反序列化讀取。

目的:驗證readObjectNoData()標簽方法結果。

結果:反序列化catFamily.txt文件,得出結果 white <---->tiger。

總結:實現readObjectNoData()標簽方法。

#### 3)第三種(第二次改變對比第一次改變)

改變之處:

- 改變父類CatFamily類,去掉實現序列化Serializable接口;
- 子類Cat類依然繼承父類CatFamily類,并且直接實現序列化Serializable接口;
- 測試類對catFamily.txt進行反序列化讀取。

目的:驗證父類未實現序列化Serializable接口時,readObjectNoData()方法是否繼續(xù)有效。

結果:反序列化catFamily.txt文件,得出結果 white <---->null 。

總結:readObjectNoData()方法沒有得到體現。

#### 4)第四種(第三次改變對比未改變)

改變之處:

- Cat類去掉父類CatFamily類,自身直接實現序列化Serializable接口;
- Cat類實現readObjectNoData()方法;
- 測試類對catFamily.txt進行反序列化讀取。

目的:測試readObjectNoData()方法的作用域。

結果:反序列化catFamily.txt文件,得出結果 white <---->null。

總結:readObjectNoData()方法作用域為寫入catFamily.txt文件的對象Object的實體類的實現序列化Serializable接口的父類。

### 2.4 推測總結:

- readObjectNoData()標簽方法作用域為進行序列化對象的父類,并且其父類必須實現了序列化接口Serializable;
- readObjectNoData()標簽方法在上面測試的代碼中體現作用類似于set屬性;
- readObjectNoData()標簽方法內set的屬性值為該類的屬性值,也就是說當引用其他對象屬性值進行set時,該方法是無效的。

### 3.1 翻譯文檔  

>Serializable classes that need to designate an alternative object to be used when writing an object to the stream should implement this special method with the exact signature:ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;

實現序列化的類,其Object對象被指定另外一個實現序列化非此類的對象進行替換的時候,在進行把該實體Object對象寫入到數據流中時,需要實現Object writeReplace() throws ObjectStreamException;這個特殊的標簽方法。

### 3.2 代碼驗證

注意:替換類和被替換類都需要實現序列化接口,否則在寫入(writeObject)時會拋出java.io.NotSerializableException異常,且被替換類為徹底被替換。

#### 1)測試writeReplace()標簽方法

實體類:
import java.io.ObjectStreamException;
import java.io.Serializable;

public class Dog implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = -4094903168892128473L;

    private String type;

    private String color;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
    private Object writeReplace() throws ObjectStreamException {
        Wolf wolf = new Wolf();
        wolf.setType(type);
        wolf.setColor(color);
        return wolf;
    }
}

class Wolf implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = -1501152003733531169L;

    private String type;

    private String color;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}
測試類:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class DogTest {

    public static void serializable() throws IOException{
        Dog dog = new Dog();
        dog.setColor("white");
        dog.setType("Chinese garden dog");
        FileOutputStream fos = new FileOutputStream("dog.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(dog);
        oos.flush();
        oos.close();
    }

    public static void deSerializable() throws IOException,ClassNotFoundException{
        FileInputStream sfis = new FileInputStream("dog.txt");
        ObjectInputStream sois = new ObjectInputStream(sfis);
        Wolf wolf = (Wolf) sois.readObject();
        System.out.println(wolf.getType() +"<------->"+ wolf.getColor());
    }

    public static void main(String[] args) throws IOException ,ClassNotFoundException{
        serializable();
        deSerializable();
    }
}
代碼實現結果:Chinese garden dog<------->white。

#### 2)測試是否被徹底替換

代碼說明:實體類不修改,只修改測試類的反序列化方法,在readObject()方法時由Wolf對象轉變?yōu)镈og對象。
public static void deSerializable() throws IOException,ClassNotFoundException{
    FileInputStream sfis = new FileInputStream("dog.txt");
    ObjectInputStream sois = new ObjectInputStream(sfis);
    Dog dog = (Dog) sois.readObject();
    System.out.println(dog.getType() +"<------->"+ dog.getColor());
}
代碼實現結果:
    (
      第25行:Dog dog = (Dog) sois.readObject(); 
      第32行:deSerializable();
     )
    Exception in thread "main" java.lang.ClassCastException: com.springboot.SpringBootDemo.serializable.Wolf cannot be cast to com.springboot.SpringBootDemo.serializable.Dog
at com.springboot.SpringBootDemo.serializable.DogTest.deSerializable(DogTest.java:25)
at com.springboot.SpringBootDemo.serializable.DogTest.main(DogTest.java:32)
序列化對象為Dog對象,而反序列化依然通過Dog對象,結果發(fā)生異常,此時可知在序列化時Dog對象被Wolf對象給替換了。

### 4.1 翻譯文檔

>This writeReplace method is invoked by serialization if the method exists and it would be accessible from a method defined within the class of the object being serialized. Thus, the method can have private,protected and package-private access. Subclass access to this method follows java accessibility rules.

在序列化對象時,其類的方法中如果有writeReplace標簽方法存在的話,則該標簽方法會在序列化寫入時被調用。因此該方法可以具有private,protected和package-private訪問。該類的子類訪問該方法時會遵循java可訪問性規(guī)則。

### 4.2 代碼驗證

注意:

- 父類實現writeReplace標簽方法;
- 子類擁有訪問writeReplace標簽方法的權限。

#### 1)實體類
import java.io.ObjectStreamException;
import java.io.Serializable;

public class Dog implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = -4094903168892128473L;

    private String type;

    private String color;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
    public Object writeReplace() throws ObjectStreamException {
        Wolf wolf = new Wolf();
        wolf.setType(type);
        wolf.setColor(color);
        return wolf;
    }
}

class ChineseGardenDog extends Dog {
    private float height;

    public float getHeight() {
        return height;
    }

    public void setHeight(float height) {
        this.height = height;
    }
}

class Wolf implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = -1501152003733531169L;

    private String type;

    private String color;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}
#### 2)測試類
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class DogTest {

    public static void serializable() throws IOException{
        ChineseGardenDog dog = new ChineseGardenDog();
        dog.setColor("white");
        dog.setType("Chinese garden dog");
        dog.setHeight(55);
        FileOutputStream fos = new FileOutputStream("dog.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(dog);
        oos.flush();
        oos.close();
    }

    public static void deSerializable() throws IOException,ClassNotFoundException{
        FileInputStream sfis = new FileInputStream("dog.txt");
        ObjectInputStream sois = new ObjectInputStream(sfis);
        Wolf wolf = (Wolf) sois.readObject();
        System.out.println(wolf.getType() +"<------->"+ wolf.getColor());
    }

    public static void main(String[] args) throws IOException ,ClassNotFoundException{
        serializable();
        deSerializable();
    }
}
測試結果:Chinese garden dog<------->white。

### 5.1 翻譯文檔

>Classes that need to designate a replacement when an instance of it is read from the stream should implement this special method with the exact signature.
>
>ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
>
>This readResolve method follows the same invocation rules and accessibility rules as writeReplace.

當從數據流中讀取一個實例的時候,指定替換的類需要實現此特殊方法。

ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;

readResolve標簽方法遵循與writeReplace相同的調用規(guī)則和可訪問性規(guī)則。

### 5.2 代碼驗證

注:該方法的寫入對象實例和讀取對象實例為同一個對象(適用于單例模式)。

#### 1)未實現標簽方法

實體類:
    import java.io.Serializable;
    public class Mouse implements Serializable{
        /**
         * 
         */
        private static final long serialVersionUID = -8615238438948214201L;

        private String name;

        public static Mouse INSTANCE;

        public static Mouse getInstance(){
            if(INSTANCE == null){
                INSTANCE = new Mouse();
            }
            return INSTANCE;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
測試類:
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;

    public class MouseTest {

        public static void serializable() throws IOException{
            Mouse mouse= Mouse.getInstance();
            mouse.setName("Jerry");
            FileOutputStream fos = new FileOutputStream("mouse.txt");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            System.out.println("寫入對象hash值 = "+ mouse.hashCode());
            oos.writeObject(mouse);
            oos.flush();
            oos.close();
        }

        public static void deSerializable() throws IOException,ClassNotFoundException{
            FileInputStream sfis = new FileInputStream("mouse.txt");
            ObjectInputStream sois = new ObjectInputStream(sfis);
            Mouse mouse = (Mouse) sois.readObject();
            System.out.println("讀取對象hash值 = " +mouse.hashCode());
        }

        public static void main(String[] args) throws IOException, ClassNotFoundException {
            serializable();
            deSerializable();
        }
    }
測試結果:

寫入對象hash值 = 366712642

讀取對象hash值 = 1096979270

#### 2)實現標簽方法:(測試類不變,實體類增加readResolve()方法)

實體類:
    import java.io.ObjectStreamException;
    import java.io.Serializable;

    public class Mouse implements Serializable{
        /**
         * 
         */
        private static final long serialVersionUID = -8615238438948214201L;

        private String name;

        public static Mouse INSTANCE;

        public static Mouse getInstance(){
            if(INSTANCE == null){
                INSTANCE = new Mouse();
            }
            return INSTANCE;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        private Object readResolve() throws ObjectStreamException{
            return INSTANCE;
        }
    }
測試結果:

寫入對象hash值 = 366712642

讀取對象hash值 = 366712642

推測:指定寫入的對象實例和讀取指定的對象實例為同一個。

### 6.1 翻譯文檔

>The serialization runtime associates with each serializable class a version number, called a serialVersionUID, which is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization. If the receiver has loaded a class for the object that has a different serialVersionUID than that of the corresponding sender's class, then deserialization will result in an {@link InvalidClassException}.  A serializable class can declare its own serialVersionUID explicitly by declaring a field named <code>"serialVersionUID"</code> that must be static, final, and of type <code>long</code>: 
>
>ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
>
>If a serializable class does not explicitly declare a serialVersionUID, then the serialization runtime will calculate a default serialVersionUID value for that class based on various aspects of the class, as described in the Java(TM) Object Serialization Specification.  However, it is <em>strongly recommended</em> that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected <code>InvalidClassException</code>s during deserialization.  Therefore, to guarantee a consistent serialVersionUID value across different java compiler implementations, a serializable class must declare an explicit serialVersionUID value.  It is also strongly advised that explicit serialVersionUID declarations use the <code>private</code> modifier where possible, since such declarations apply only to the immediately declaring class--serialVersionUID fields are not useful as inherited members. Array classes cannot declare an explicit serialVersionUID, so they always have the default computed value, but the requirement for matching serialVersionUID values is waived for array classes.

### 6.2 針對實現Serializable接口 代碼驗證

父類:Person類
public class Person {

    public String name;

    public String gender;

    public int age;

    float height;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public float getHeight() {
        return height;
    }

    public void setHeight(float height) {
        this.height = height;
    }
}
子類:Male類
import java.io.Serializable;

public class Male extends Person implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = -7361904256653535728L;

    public boolean beard;

    public boolean havaBeard(int age){
        boolean flag = false;

        if(age>=18){
            flag = true;
        }
        return flag;
    }

    public boolean isBeard() {
        return beard;
    }

    public void setBeard(boolean beard) {
        this.beard = beard;
    }
}
三級子類:Students類
public class Students extends Male{

    private static final long serialVersionUID = -6982821977091370834L;

    public String stuCard;

    private int grades;

    public String getStuCard() {
        return stuCard;
    }

    public void setStuCard(String stuCard) {
        this.stuCard = stuCard;
    }

    public int getGrades() {
        return grades;
    }

    public void setGrades(int grades) {
        this.grades = grades;
    }
}
類:Female類
import java.io.Serializable;

public class Female implements Serializable{

    private static final long serialVersionUID = 6907419491408608648L;

    public String name;

    public String gender;

    public int age;

    float height;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public float getHeight() {
        return height;
    }

    public void setHeight(float height) {
        this.height = height;
    }
}
測試類:SubTypeSerializable
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SubTypeSerializable {

public static void main(String[] args) throws IOException, ClassNotFoundException{

    /**(一)、Person實體類,未實現序列化接口,無父類*/
    FileOutputStream fo = new FileOutputStream("person.txt");
    ObjectOutputStream ss = new ObjectOutputStream(fo);
    Person person = new Person();
    person.setAge(100);
    person.setGender("性別");
    person.setHeight(165);
    person.setName("人類");
    ss.writeObject(person);
    ss.flush();
    ss.close();

    //反序列化
    FileInputStream sfis = new FileInputStream("person.txt");
    ObjectInputStream sois = new ObjectInputStream(sfis);
    Person ps = (Person) sois.readObject();
    System.out.println(ps.getName() +" "+ps.getGender()+" "+ps.getHeight() +" "+ps.getAge());

    /**結果:
        在執(zhí)行writeObject(person)是發(fā)生異常
    Exception in thread "main" java.io.NotSerializableException:
        com.springboot.SpringBootDemo.serializable.Person
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.writeObject(Unknown Source)
        at com.springboot.SpringBootDemo.serializable.SubTypeSerializable.main(SubTypeSerializable.java:21)
     Exception in thread "main" java.io.WriteAbortedException: writing aborted;    java.io.NotSerializableException: com.springboot.SpringBootDemo.serializable.Person
        at java.io.ObjectInputStream.readObject0(Unknown Source)
        at java.io.ObjectInputStream.readObject(Unknown Source)
        at com.springboot.SpringBootDemo.serializable.SubTypeSerializable.main(SubTypeSerializable.java:28)
     * */
    System.out.println("<--------------------------------------------------------------------------->");

    /**(二)、Male繼承父類Person,自身實現序列化接口,其父類Person沒有實現序列化接口*/
    FileOutputStream fos = new FileOutputStream("male.txt");
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    Male male = new Male();
    /**
     * 其父類的父類Person的屬性
     * 
     * public String name;
       public String gender;
       public int age;
       float height;
     * */
    male.setName("張三");
    male.setGender("男性");
    male.setAge(25);
    male.setHeight(175);
    /**
     * 其自身屬性
     * public boolean beard;
     * */
    male.setBeard(true);
    oos.writeObject(male);
    oos.flush();
    oos.close();

    //反序列化
    FileInputStream fis = new FileInputStream("male.txt");
    ObjectInputStream ois = new ObjectInputStream(fis);
    Male ml = (Male) ois.readObject();
    System.out.println(ml.getName() +" "+ml.getGender()+" "+ml.getHeight() +" "+ml.getAge()+" "+male.isBeard());

    /**結果:
     * 父類沒有被序列化,唯獨子類屬性被序列化
     * 
     * ml.getName() == null 
     * ml.getGender() == null 
     * ml.getHeight() == 0.0 
     * ml.getAge() == 0 
     * male.isBeard() == true
     * 父類屬性:
     *  public String name;
        public String gender;
        public int age;
        float height;
                   均未實現序列化;
                自身屬性:
        public boolean beard;
                    實現序列化        
     * */

    System.out.println("<--------------------------------------------------------------------------->");

    /**(三)、Female實現序列化接口,無父類*/
    FileOutputStream ffos = new FileOutputStream("female.txt");
    ObjectOutputStream foos = new ObjectOutputStream(ffos);
    Female female = new Female();
    /**
     * 其自身的屬性
     * public String name;
       public String gender;
       public int age;
       float height;
     **/
    female.setAge(25);
    female.setGender("女性");
    female.setHeight(165);
    female.setName("張芳");
    foos.writeObject(female);
    foos.flush();
    foos.close();

    //反序列化
    FileInputStream ffis = new FileInputStream("female.txt");
    ObjectInputStream fois = new ObjectInputStream(ffis);
    Female fm = (Female) fois.readObject();
    System.out.println(fm.getName() +" "+fm.getGender()+" "+fm.getHeight() +" "+fm.getAge());

    /**結果:
     * 自身屬性均實現序列化
     * 
     * fm.getName() == 張芳
     * fm.getGender() == 女性 
     * fm.getHeight() == 165.0 
     * fm.getAge() == 25
     * 所有屬性均實現序列化*/
    System.out.println("<--------------------------------------------------------------------------->");

    /**(四)、Students未實現序列化接口,繼承父類Male,其父類繼承父類Person,自身實現序列化接口,其父類Person沒有實現序列化接口*/
    FileOutputStream stufos = new FileOutputStream("students.txt");
    ObjectOutputStream stuoos = new ObjectOutputStream(stufos);
    Students students = new Students();
    /**
     * 其父類的父類Person的屬性
     * 
     * public String name;
       public String gender;
       public int age;
       float height;
     * */
    students.setName("王小明");
    students.setGender("男性");
    students.setAge(15);
    students.setHeight(160);
    /**
     * 其父類Male屬性
     * public boolean beard;
     * */
    students.setBeard(true);
    /**
     * 自身屬性
     * public String stuCard;
       private int grades;
     * */
    students.setStuCard("1234567890987");
    students.setGrades(300);
    stuoos.writeObject(students);
    stuoos.flush();
    stuoos.close();

    //反序列化
    FileInputStream stufis = new FileInputStream("students.txt");
    ObjectInputStream stuois = new ObjectInputStream(stufis);
    Students st = (Students) stuois.readObject();
    System.out.println(st.getName() +" "+st.getGender()+" "+st.getAge()+" "+st.getHeight()+" "+st.isBeard()+" "+st.getStuCard()+" "+st.getGrades());

    /**結果:
     * 父類的父類屬性未實現序列化,父類實現序列化,自身實現序列化
     * st.getName() == null 
     * st.getGender() == null 
     * st.getAge() == 0 
     * st.getHeight() == 0.0 
     * st.isBeard() == true 
     * st.getStuCard() == 1234567890987 
     * st.getGrades() == 300
     * 自身public String stuCard;
          private int grades;
            實現序列化;
           而父類Male屬性
          public boolean beard
            實現序列化;
           父類的父類Person
        public String name;
        public String gender;
        public int age;
        float height;
                   未實現序列化
     * */
    }
}
### 6.3 回顧

1)在使用ObjectInputStream、ObjectOutputStream對對象進行寫入寫出時,其寫入的對象的類需要實現java.io.Serializable序列化接口,否則會報出 writeObject()異常:
        Exception in thread "main" java.io.NotSerializableException: com.springboot.SpringBootDemo.serializable.Person
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.writeObject(Unknown Source)
        at com.springboot.SpringBootDemo.serializable.SubTypeSerializable.main(SubTypeSerializable.java:21)
        readObject()異常: 
        Exception in thread "main" java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: com.springboot.SpringBootDemo.serializable.Person
            java.io.ObjectInputStream.readObject0(Unknown Source)
            at java.io.ObjectInputStream.readObject(Unknown Source)
            at com.springboot.SpringBootDemo.serializable.SubTypeSerializable.main(SubTypeSerializable.java:28)
2)父類未實現java.io.Serializable序列化接口,其子類依然可以進行序列化,但其子類進行對象序列化讀寫時,父類無法被序列化,只能自身實現序列化;

3)自身實現java.io.Serializable序列化接口,在進行對象讀寫時會被實現序列化;

4)父類實現java.io.Serializable序列化接口,其子類不需要再次申明實現序列化,子類在進行對象序列化讀寫時,父類和子類均被實現序列化。

### 7.1 總結

#### 1)java.io.Serializable接口

首先,Serializable類是一個接口,所以對象的序列化并不是Serializable來實現的;

其次,Serializable是一個標簽,各種序列化類在讀取到這個標簽的時候,會按照自己的方式進行序列化。

#### 2)序列化是干什么的,為什么需要序列化

我們知道,當兩個進程進行遠程通信時,可以相互發(fā)送各種類型的數據,包括文本、圖片、音頻、視頻等,而這些數據都會以二進制序列的形式在網絡上傳送。

那么當兩個Java進程進行通信時,能否實現進程間的對象傳送呢?答案是可以的!如何做到呢?這就需要Java序列化與反序列化了!

換句話說:一方面,發(fā)送方需要把這個Java對象轉換為字節(jié)序列,然后在網絡上傳送;另一方面,接收方需要從字節(jié)序列中恢復出Java對象。

當我們明晰了為什么需要Java序列化和反序列化后,我們很自然地會想Java序列化的好處。

- 實現了數據的持久化,通過序列化可以記錄下數據結構或者對象的狀態(tài)(也就是實體變量Object[注:不是Class]某一個時間點的值),把數據臨時或者永久地保存到硬盤上(通常存放在文件里);
- 利用序列化實現遠程通信,并且在數據傳遞過程中或者使用時能夠保證數據結構或者對象狀態(tài)的完整性和可傳遞性,即在網絡上傳送對象的字節(jié)序列。

>作者:忠勝
>
>首發(fā):「野指針」
>
>來源:宜信技術學院

性質:接口類

package java.io

public interface Serializable

1.1 翻譯文檔

Serializability of a class is enabled by the class implementing the java.io.Serializable interface.

通過實現java.io.Serializable interface接口來序列化一個類。

Classes that do not implement this interface will not have any of their state serialized or deserialized.

沒有實現此接口的類任何狀態(tài)都不會序列化或反序列化。

All subtypes of a serializable class are themselves serializable.

可序列化類的所有子類而本身都是可序列化的。

The serialization interface has no methods or fields and serves only to identify the semantics of being serializable.

序列化接口沒有方法或字段域,它僅用來標識可序列化的語義。

(1)To allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype's public, protected, and (if accessible) package fields.

(2)The subtype may assume this responsibility only if the class it extends has an accessible no-arg constructor to initialize the class's state.  It is an error to declare a class Serializable if this is not the case.  The error will be detected at runtime.

(3)During deserialization, the fields of non-serializable classes will be initialized using the public or protected no-arg constructor of the class.  A no-arg constructor must be accessible to the subclass that is serializable.  The fields of serializable subclasses will be restored from the stream.

(1)為了讓非序列化類的子類可以被序列化,這個子類可以承擔保存和恢復超類的pulic,protected,package字段(如果可訪問的話)。

(2)只有當它拓展的類具有可訪問無參構造函數來初始化類的狀態(tài)時,子類才可以承擔這樣的責任。如果不是這種情況,就不能聲明一個類是可序列化的。這個錯誤將在運行的時候被檢測出來。

(3)在反序列化期間,非序列化類的字段將通過類的以public或者protected修飾的空參構造函數實例化。無參數構造函數必須可訪問可序列化的子類。序列化子類的字段能夠從字符流里被還原。

1.2 輔助理解

(1)(2)(3)三塊主要說了三件事:

  • 非序列化的父類,其子類實現序列化時承擔保存和恢復父類public、protected、package等子類可訪問到子類的字段;
  • 非序列化的父類,其子類進行序列化時,父類需要有用public或者protected修飾的空參構造函數;
  • 若無空參構造函數的父類,其子類在運行序列化時將正常進行,但反序列化時會發(fā)生錯誤,并拋出異常。但父類有空參構造函數,子類完成序列化,父類屬性卻沒有參與到序列化中。

1.3 注意:此處有三個坑。

  • (1)中所述父類未實現序列化,實現序列化的子類會承擔保存和恢復父類的public、protected、package等子類可訪問到子類的字段。此處我個人理解為實現序列化的子類進行序列化的時候繼承了未實現序列化的父類中子類可訪問到的屬性,但序列化時無法記錄下父類對象的狀態(tài)信息;
  • 此處文檔若要正確讀取理解,切記(1)(2)(3)不可拆分,要放在一起去理解(上文之所以分開是便于翻譯);
  • 此處英文翻譯成漢字,難以理解其真實含義,所以通過下面的代碼驗證來輔助理解

1.4 代碼驗證

輔以A/B兩套類型代碼對比理解:

1)A套

父類:Biology 類

package com.springboot.SpringBootDemo.serializable;

            public class Biology {

                public String type;

                private int num;

                public Biology(String type, int num) {
                    this.type = type;
                    this.num = num;
                }

                public String getType() {
                    return type;
                }

                public void setType(String type) {
                    this.type = type;
                }

                public int getNum() {
                    return num;
                }

                public void setNum(int num) {
                    this.num = num;
                }
            }

子類:People 類

package com.springboot.SpringBootDemo.serializable;

            import java.io.Serializable;

            public class People extends Biology implements Serializable{

                private static final long serialVersionUID = -6623611040000763479L;

                public String name;

                protected String gender;

                private int age;

                public People(String type, int num, String name ,String gender ,int age) {
                    super(type, num);
                    this.name = name;
                    this.gender = gender;
                    this.age = age;
                }

                public String getName() {
                    return name;
                }

                public void setName(String name) {
                    this.name = name;
                }

                public String getGender() {
                    return gender;
                }

                public void setGender(String gender) {
                    this.gender = gender;
                }

                public int getAge() {
                    return age;
                }

                public void setAge(int age) {
                    this.age = age;
                }
            }

測試類:

           import java.io.FileInputStream;
            import java.io.FileOutputStream;
            import java.io.IOException;
            import java.io.ObjectInputStream;
            import java.io.ObjectOutputStream;

            public class Test {

                public static void main(String[] args) throws IOException, ClassNotFoundException {
                    People pp = new People("human",10000,"張三","男",25);

                    FileOutputStream fos = new FileOutputStream("test.txt");
                    ObjectOutputStream oos = new ObjectOutputStream(fos);
                    oos.writeObject(pp);
                    oos.flush();
                    oos.close();

                    //反序列化
                    FileInputStream sfis = new FileInputStream("test.txt");
                    ObjectInputStream sois = new ObjectInputStream(sfis);
                    People p = (People) sois.readObject();
                    System.out.println(
                            p.getType() +" "+
                            p.getNum() +" "+
                            p.getName() +" "+
                            p.getGender() +" "+
                            p.getAge()
                            );
                }
            }

結果:

   Exception in thread "main" java.io.InvalidClassException: com.springboot.SpringBootDemo.serializable.People; no valid constructor
                at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(Unknown Source)
                at java.io.ObjectStreamClass.checkDeserialize(Unknown Source)
                at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
                at java.io.ObjectInputStream.readObject0(Unknown Source)
                at java.io.ObjectInputStream.readObject(Unknown Source)
                at com.springboot.SpringBootDemo.serializable.Test.main(Test.java:23)

結果說明:在序列化時未發(fā)生異常,而在反序列化readObject()時發(fā)生異常。也就是說,父類沒有無參構造函數時,序列化正常進行,但反序列化時拋出newInvalidClassException異常。

2)B套

父類:Person類

            public class Person {

                public String name;

                public String gender;

                public int age;

                float height;

                public String getName() {
                    return name;
                }

                public void setName(String name) {
                    this.name = name;
                }

                public String getGender() {
                    return gender;
                }

                public void setGender(String gender) {
                    this.gender = gender;
                }

                public int getAge() {
                    return age;
                }

                public void setAge(int age) {
                    this.age = age;
                }

                public float getHeight() {
                    return height;
                }

                public void setHeight(float height) {
                    this.height = height;
                }
            }

子類:Male類

        import java.io.Serializable;

        public class Male extends Person implements Serializable{
            /**
             * 
             */
            private static final long serialVersionUID = -7361904256653535728L;

            public boolean beard;

            protected String weight;

            public boolean havaBeard(int age){
                boolean flag = false;

                if(age>=18){
                    flag = true;
                }
                return flag;
            }

            public boolean isBeard() {
                return beard;
            }

            public void setBeard(boolean beard) {
                this.beard = beard;
            }

            public String getWeight() {
                return weight;
            }

            public void setWeight(String weight) {
                this.weight = weight;
            }

        }

測試類:

        import java.io.FileInputStream;
        import java.io.FileOutputStream;
        import java.io.IOException;
        import java.io.ObjectInputStream;
        import java.io.ObjectOutputStream;

        public class SubTypeSerializable {

            public static void main(String[] args) throws IOException, ClassNotFoundException{

                /**Male繼承父類Person,自身實現序列化接口,其父類Person沒有實現序列化接口*/
                FileOutputStream fos = new FileOutputStream("male.txt");
                ObjectOutputStream oos = new ObjectOutputStream(fos);
                Male male = new Male();
                /**
                 * 其父類的父類Person的屬性
                 * 
                 * public String name;
                   public String gender;
                   public int age;
                   float height;
                 * */
                male.setName("張三");
                male.setGender("男性");
                male.setAge(25);
                male.setHeight(175);
                /**
                 * 其自身屬性
                 * public boolean beard;
                 * */
                male.setBeard(true);
                oos.writeObject(male);
                oos.flush();
                oos.close();

                //反序列化
                FileInputStream fis = new FileInputStream("male.txt");
                ObjectInputStream ois = new ObjectInputStream(fis);
                Male ml = (Male) ois.readObject();
                System.out.println(ml.getName() +" "+ml.getGender()+" "+ml.getHeight() +" "+ml.getAge()+" "+male.isBeard());
            }
    }          

結果:

    ml.getName() == null 
    ml.getGender() == null 
    ml.getHeight() == 0.0 
    ml.getAge() == 0 
    male.isBeard() == true

1.5 測試分析

1)父類屬性

public String name;

public String gender;

public int age;

float height;

其狀態(tài)信息均未被記錄;

2)自身屬性

public boolean beard;

其狀態(tài)信息被記錄

2.1 翻譯文檔

When traversing a graph, an object may be encountered that does not support the Serializable interface. In this case the NotSerializableException will be thrown and will identify the class of the non-serializable object.

當循環(huán)遍歷一個數據結構圖(數據結構圖可理解為數據結構類型,比如二叉樹)的時候,對象可能會遭遇到不支持實現序列化接口的情景。在這種情況下,將發(fā)生拋出NotSerializableException異常,并且該類被定義為不可序列化類。

Classes that require special handling during the serialization and deserialization process must implement special methods with these exact signatures:

在實現序列化和反序列化過程中,特殊處理的類需要實現這些特殊的方法:

```<PRE>
private void writeObject(java.io.ObjectOutputStream out) throws IOException

private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;

private void readObjectNoData() throws ObjectStreamException;

>The writeObject method is responsible for writing the state of the object for its particular class so that the corresponding readObject method can restore it.  The default mechanism for saving the Object's fields can be invoked by calling out.defaultWriteObject. The method does not need to concern itself with the state belonging to its superclasses or subclasses. State is saved by writing the individual fields to the ObjectOutputStream using the writeObject method or by using the methods for primitive data types supported by DataOutput.

witeObject方法負責寫入特定類的Object對象狀態(tài)信息,readObject方法可以還原該Object對象的狀態(tài)信息。保存該Object對象字段的默認機制是通過調用out.defaultWriteObject來實現。該方法不需要關注屬于其超類或子類的狀態(tài)。通過使用writeObject方法將各個字段寫入ObjectOutputStream,或使用DataOutput支持的基本數據類型的方法來保存狀態(tài)。

>The readObject method is responsible for reading from the stream and restoring the classes fields. It may call in.defaultReadObject to invoke the default mechanism for restoring the object's non-static and non-transient fields.  The defaultReadObject method uses information in the stream to assign the fields of the object saved in the stream with the correspondingly named fields in the current object.  This handles the case  when the class has evolved to add new fields. The method does not need to  concern itself with the state belonging to its superclasses or subclasses. State is saved by writing the individual fields to the ObjectOutputStream using the writeObject method or by using the methods for primitive data types supported by DataOutput.

readObject方法是負責讀取數據流并恢復該類的字段。它可以通過調用in.defaultReadObject來恢復非static以及非transient修飾的字段。defaultReadObject方法通過數據流中的信息,把當前類保存在數據流中的字段信息分配到相對應的字段名(也就是說把字段的值分配給相對應的字段名)。這種處理方式也能處理該類新增字段的情況。該方法不需要關注屬于其超類或子類的狀態(tài)。通過使用writeObject方法將各個字段寫入ObjectOutputStream,或使用DataOutput支持的基本數據類型的方法來保存狀態(tài)。通過writeObject方法把對象Object的各個字段寫入到ObjectOutputStream中,或者通過使用DataOutput支持的基本數據類型的方法來保存該Object對象的狀態(tài)信息。

>The readObjectNoData method is responsible for initializing the state of the object for its particular class in the event that the serialization stream does not list the given class as a superclass of the object being deserialized.  This may occur in cases where the receiving party uses a different version of the deserialized instance's class than the sending party, and the receiver's version extends classes that are not extended by the sender's version.  This may also occur if the serialization stream has been tampered; hence, readObjectNoData is useful for initializing deserialized objects properly despite a "hostile" or incomplete source stream.

(該處翻譯有些吃力,所以直接軟件翻譯,會后續(xù)進行代碼驗證體悟)

當出現反序列化與序列化類的版本不一致的情況時,readObjectNoData()標簽方法負責初始化對象的字段值。這種情況可能發(fā)生在反序列化時,接收方使用了發(fā)送方對象的類的不同版本,或者接收方繼承的類的版本與發(fā)送方繼承的類的版本不一致。另外,當序列化流被篡改了,也會發(fā)生這種情況。因此,當出現類不一致或者反序列化流不完全的情況時,readObjectNoData初始化反序列化對象的字段就非常有用了。

### 2.2 代碼驗證

#### 1)改變之前
public class Cat implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = -5731096200028489933L;

    public String color;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}
改變之前測試類:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class CatFamilylTest {

    public static void main(String[] args) throws Exception {
        serializable();
        deSerializable();
    }

    public static void serializable() throws Exception{
        Cat cat = new Cat();
        cat.setColor("white");
        FileOutputStream fos = new FileOutputStream("catFamily.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(cat);
        oos.flush();
        oos.close();
    }

    public static void deSerializable() throws Exception{
        FileInputStream sfis = new FileInputStream("catFamily.txt");
        ObjectInputStream sois = new ObjectInputStream(sfis);
        Cat cat = (Cat) sois.readObject();
        System.out.println(cat.getColor());
    }
}
結果:white

#### 2)第一次改變

第一次改變之增加父類:
import java.io.Serializable;

public class CatFamily implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = -7796480232179180594L;
    public String catType;

    public String getCatType() {
        return catType;
    }

    public void setCatType(String catType) {
        this.catType = catType;
    }

    private void readObjectNoData() {
        this.catType = "tiger";                 
    } 
}
第一次改變之后之Cat類變化:
public class Cat extends CatFamily{
    /**
     * 
     */
    private static final long serialVersionUID = -5731096200028489933L;

    public String color;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}
第一次改變之讀取已經存在的catFamily.txt文件:
public class CatFamilylTest {

    public static void main(String[] args) throws Exception {
        deSerializable();
    }
    public static void deSerializable() throws Exception{
        FileInputStream sfis = new FileInputStream("catFamily.txt");
        ObjectInputStream sois = new ObjectInputStream(sfis);
        Cat cat = (Cat) sois.readObject();
        System.out.println(cat.getColor()+" <---->"+cat.getCatType());
    }
}
第一次改變之結果:white <---->tiger

#### 3)第二次改變測試

第二次改變之父類:
public class CatFamily{

    public String catType;

    public String getCatType() {
        return catType;
    }

    public void setCatType(String catType) {
        this.catType = catType;
    }

    private void readObjectNoData() {
        this.catType = "tiger";                 
    } 
}
第二次改變之Cat類:
import java.io.Serializable;

public class Cat extends CatFamily implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = -5731096200028489933L;

    public String color;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}
第二次改變之測試類:
public class CatFamilylTest {

    public static void main(String[] args) throws Exception {
        deSerializable();

    }
    public static void deSerializable() throws Exception{
        FileInputStream sfis = new FileInputStream("catFamily.txt");
        ObjectInputStream sois = new ObjectInputStream(sfis);
        Cat cat = (Cat) sois.readObject();
        System.out.println(cat.getColor()+" <---->"+cat.getCatType());
    }
}
第二次改變之結果:white <---->null

#### 4)第三次改變舉例對比驗證

第三次改變之拋棄父類,且Cat類改變:
import java.io.Serializable;

public class Cat implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = -5731096200028489933L;

    public String type;

    public String color;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    private void readObjectNoData() {
        this.type = "hellokitty";                 
    }
}
第三次改變之測試類:
public class CatFamilylTest {

    public static void main(String[] args) throws Exception {
        deSerializable();
    }

    public static void deSerializable() throws Exception{
        FileInputStream sfis = new FileInputStream("catFamily.txt");
        ObjectInputStream sois = new ObjectInputStream(sfis);
        Cat cat = (Cat) sois.readObject();
        System.out.println(cat.getColor()+" <---->"+cat.getType());
    }
}
第三次改變之測試結果:white <---->null

### 2.3 測試代碼描述

#### 1)第一種(改變之前)

描述:建立實現序列化接口的Cat類,以及對應的測試類生產文件catFamily.txt

兩個目的:

- 用于建立變化的基礎層代碼;
- 生成序列化后的文件。

結果:反序列化catFamily.txt文件,得出正常結果 write 。

#### 2)第二種(第一次改變對比未改變)

改變之處:

- 增加實現序列化接口的父類CatFamily類,增添readObjectNoData()方法,并且設置屬性字段catType值為tiger;
- Cat類不直接實現序列化Serializable接口,而是繼承CatFamily類;
- 測試類對catFamily.txt進行反序列化讀取。

目的:驗證readObjectNoData()標簽方法結果。

結果:反序列化catFamily.txt文件,得出結果 white <---->tiger。

總結:實現readObjectNoData()標簽方法。

#### 3)第三種(第二次改變對比第一次改變)

改變之處:

- 改變父類CatFamily類,去掉實現序列化Serializable接口;
- 子類Cat類依然繼承父類CatFamily類,并且直接實現序列化Serializable接口;
- 測試類對catFamily.txt進行反序列化讀取。

目的:驗證父類未實現序列化Serializable接口時,readObjectNoData()方法是否繼續(xù)有效。

結果:反序列化catFamily.txt文件,得出結果 white <---->null 。

總結:readObjectNoData()方法沒有得到體現。

#### 4)第四種(第三次改變對比未改變)

改變之處:

- Cat類去掉父類CatFamily類,自身直接實現序列化Serializable接口;
- Cat類實現readObjectNoData()方法;
- 測試類對catFamily.txt進行反序列化讀取。

目的:測試readObjectNoData()方法的作用域。

結果:反序列化catFamily.txt文件,得出結果 white <---->null。

總結:readObjectNoData()方法作用域為寫入catFamily.txt文件的對象Object的實體類的實現序列化Serializable接口的父類。

### 2.4 推測總結:

- readObjectNoData()標簽方法作用域為進行序列化對象的父類,并且其父類必須實現了序列化接口Serializable;
- readObjectNoData()標簽方法在上面測試的代碼中體現作用類似于set屬性;
- readObjectNoData()標簽方法內set的屬性值為該類的屬性值,也就是說當引用其他對象屬性值進行set時,該方法是無效的。

### 3.1 翻譯文檔  

>Serializable classes that need to designate an alternative object to be used when writing an object to the stream should implement this special method with the exact signature:ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;

實現序列化的類,其Object對象被指定另外一個實現序列化非此類的對象進行替換的時候,在進行把該實體Object對象寫入到數據流中時,需要實現Object writeReplace() throws ObjectStreamException;這個特殊的標簽方法。

### 3.2 代碼驗證

注意:替換類和被替換類都需要實現序列化接口,否則在寫入(writeObject)時會拋出java.io.NotSerializableException異常,且被替換類為徹底被替換。

#### 1)測試writeReplace()標簽方法

實體類:
import java.io.ObjectStreamException;
import java.io.Serializable;

public class Dog implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = -4094903168892128473L;

    private String type;

    private String color;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
    private Object writeReplace() throws ObjectStreamException {
        Wolf wolf = new Wolf();
        wolf.setType(type);
        wolf.setColor(color);
        return wolf;
    }
}

class Wolf implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = -1501152003733531169L;

    private String type;

    private String color;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}
測試類:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class DogTest {

    public static void serializable() throws IOException{
        Dog dog = new Dog();
        dog.setColor("white");
        dog.setType("Chinese garden dog");
        FileOutputStream fos = new FileOutputStream("dog.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(dog);
        oos.flush();
        oos.close();
    }

    public static void deSerializable() throws IOException,ClassNotFoundException{
        FileInputStream sfis = new FileInputStream("dog.txt");
        ObjectInputStream sois = new ObjectInputStream(sfis);
        Wolf wolf = (Wolf) sois.readObject();
        System.out.println(wolf.getType() +"<------->"+ wolf.getColor());
    }

    public static void main(String[] args) throws IOException ,ClassNotFoundException{
        serializable();
        deSerializable();
    }
}
代碼實現結果:Chinese garden dog<------->white。

#### 2)測試是否被徹底替換

代碼說明:實體類不修改,只修改測試類的反序列化方法,在readObject()方法時由Wolf對象轉變?yōu)镈og對象。
public static void deSerializable() throws IOException,ClassNotFoundException{
    FileInputStream sfis = new FileInputStream("dog.txt");
    ObjectInputStream sois = new ObjectInputStream(sfis);
    Dog dog = (Dog) sois.readObject();
    System.out.println(dog.getType() +"<------->"+ dog.getColor());
}
代碼實現結果:
    (
      第25行:Dog dog = (Dog) sois.readObject(); 
      第32行:deSerializable();
     )
    Exception in thread "main" java.lang.ClassCastException: com.springboot.SpringBootDemo.serializable.Wolf cannot be cast to com.springboot.SpringBootDemo.serializable.Dog
at com.springboot.SpringBootDemo.serializable.DogTest.deSerializable(DogTest.java:25)
at com.springboot.SpringBootDemo.serializable.DogTest.main(DogTest.java:32)
序列化對象為Dog對象,而反序列化依然通過Dog對象,結果發(fā)生異常,此時可知在序列化時Dog對象被Wolf對象給替換了。

### 4.1 翻譯文檔

>This writeReplace method is invoked by serialization if the method exists and it would be accessible from a method defined within the class of the object being serialized. Thus, the method can have private,protected and package-private access. Subclass access to this method follows java accessibility rules.

在序列化對象時,其類的方法中如果有writeReplace標簽方法存在的話,則該標簽方法會在序列化寫入時被調用。因此該方法可以具有private,protected和package-private訪問。該類的子類訪問該方法時會遵循java可訪問性規(guī)則。

### 4.2 代碼驗證

注意:

- 父類實現writeReplace標簽方法;
- 子類擁有訪問writeReplace標簽方法的權限。

#### 1)實體類
import java.io.ObjectStreamException;
import java.io.Serializable;

public class Dog implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = -4094903168892128473L;

    private String type;

    private String color;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
    public Object writeReplace() throws ObjectStreamException {
        Wolf wolf = new Wolf();
        wolf.setType(type);
        wolf.setColor(color);
        return wolf;
    }
}

class ChineseGardenDog extends Dog {
    private float height;

    public float getHeight() {
        return height;
    }

    public void setHeight(float height) {
        this.height = height;
    }
}

class Wolf implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = -1501152003733531169L;

    private String type;

    private String color;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}
#### 2)測試類
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class DogTest {

    public static void serializable() throws IOException{
        ChineseGardenDog dog = new ChineseGardenDog();
        dog.setColor("white");
        dog.setType("Chinese garden dog");
        dog.setHeight(55);
        FileOutputStream fos = new FileOutputStream("dog.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(dog);
        oos.flush();
        oos.close();
    }

    public static void deSerializable() throws IOException,ClassNotFoundException{
        FileInputStream sfis = new FileInputStream("dog.txt");
        ObjectInputStream sois = new ObjectInputStream(sfis);
        Wolf wolf = (Wolf) sois.readObject();
        System.out.println(wolf.getType() +"<------->"+ wolf.getColor());
    }

    public static void main(String[] args) throws IOException ,ClassNotFoundException{
        serializable();
        deSerializable();
    }
}
測試結果:Chinese garden dog<------->white。

### 5.1 翻譯文檔

>Classes that need to designate a replacement when an instance of it is read from the stream should implement this special method with the exact signature.
>
>ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
>
>This readResolve method follows the same invocation rules and accessibility rules as writeReplace.

當從數據流中讀取一個實例的時候,指定替換的類需要實現此特殊方法。

ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;

readResolve標簽方法遵循與writeReplace相同的調用規(guī)則和可訪問性規(guī)則。

### 5.2 代碼驗證

注:該方法的寫入對象實例和讀取對象實例為同一個對象(適用于單例模式)。

#### 1)未實現標簽方法

實體類:
    import java.io.Serializable;
    public class Mouse implements Serializable{
        /**
         * 
         */
        private static final long serialVersionUID = -8615238438948214201L;

        private String name;

        public static Mouse INSTANCE;

        public static Mouse getInstance(){
            if(INSTANCE == null){
                INSTANCE = new Mouse();
            }
            return INSTANCE;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
測試類:
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;

    public class MouseTest {

        public static void serializable() throws IOException{
            Mouse mouse= Mouse.getInstance();
            mouse.setName("Jerry");
            FileOutputStream fos = new FileOutputStream("mouse.txt");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            System.out.println("寫入對象hash值 = "+ mouse.hashCode());
            oos.writeObject(mouse);
            oos.flush();
            oos.close();
        }

        public static void deSerializable() throws IOException,ClassNotFoundException{
            FileInputStream sfis = new FileInputStream("mouse.txt");
            ObjectInputStream sois = new ObjectInputStream(sfis);
            Mouse mouse = (Mouse) sois.readObject();
            System.out.println("讀取對象hash值 = " +mouse.hashCode());
        }

        public static void main(String[] args) throws IOException, ClassNotFoundException {
            serializable();
            deSerializable();
        }
    }
測試結果:

寫入對象hash值 = 366712642

讀取對象hash值 = 1096979270

#### 2)實現標簽方法:(測試類不變,實體類增加readResolve()方法)

實體類:
    import java.io.ObjectStreamException;
    import java.io.Serializable;

    public class Mouse implements Serializable{
        /**
         * 
         */
        private static final long serialVersionUID = -8615238438948214201L;

        private String name;

        public static Mouse INSTANCE;

        public static Mouse getInstance(){
            if(INSTANCE == null){
                INSTANCE = new Mouse();
            }
            return INSTANCE;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        private Object readResolve() throws ObjectStreamException{
            return INSTANCE;
        }
    }
測試結果:

寫入對象hash值 = 366712642

讀取對象hash值 = 366712642

推測:指定寫入的對象實例和讀取指定的對象實例為同一個。

### 6.1 翻譯文檔

>The serialization runtime associates with each serializable class a version number, called a serialVersionUID, which is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization. If the receiver has loaded a class for the object that has a different serialVersionUID than that of the corresponding sender's class, then deserialization will result in an {@link InvalidClassException}.  A serializable class can declare its own serialVersionUID explicitly by declaring a field named <code>"serialVersionUID"</code> that must be static, final, and of type <code>long</code>: 
>
>ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
>
>If a serializable class does not explicitly declare a serialVersionUID, then the serialization runtime will calculate a default serialVersionUID value for that class based on various aspects of the class, as described in the Java(TM) Object Serialization Specification.  However, it is <em>strongly recommended</em> that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected <code>InvalidClassException</code>s during deserialization.  Therefore, to guarantee a consistent serialVersionUID value across different java compiler implementations, a serializable class must declare an explicit serialVersionUID value.  It is also strongly advised that explicit serialVersionUID declarations use the <code>private</code> modifier where possible, since such declarations apply only to the immediately declaring class--serialVersionUID fields are not useful as inherited members. Array classes cannot declare an explicit serialVersionUID, so they always have the default computed value, but the requirement for matching serialVersionUID values is waived for array classes.

### 6.2 針對實現Serializable接口 代碼驗證

父類:Person類
public class Person {

    public String name;

    public String gender;

    public int age;

    float height;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public float getHeight() {
        return height;
    }

    public void setHeight(float height) {
        this.height = height;
    }
}
子類:Male類
import java.io.Serializable;

public class Male extends Person implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = -7361904256653535728L;

    public boolean beard;

    public boolean havaBeard(int age){
        boolean flag = false;

        if(age>=18){
            flag = true;
        }
        return flag;
    }

    public boolean isBeard() {
        return beard;
    }

    public void setBeard(boolean beard) {
        this.beard = beard;
    }
}
三級子類:Students類
public class Students extends Male{

    private static final long serialVersionUID = -6982821977091370834L;

    public String stuCard;

    private int grades;

    public String getStuCard() {
        return stuCard;
    }

    public void setStuCard(String stuCard) {
        this.stuCard = stuCard;
    }

    public int getGrades() {
        return grades;
    }

    public void setGrades(int grades) {
        this.grades = grades;
    }
}
類:Female類
import java.io.Serializable;

public class Female implements Serializable{

    private static final long serialVersionUID = 6907419491408608648L;

    public String name;

    public String gender;

    public int age;

    float height;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public float getHeight() {
        return height;
    }

    public void setHeight(float height) {
        this.height = height;
    }
}
測試類:SubTypeSerializable
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SubTypeSerializable {

public static void main(String[] args) throws IOException, ClassNotFoundException{

    /**(一)、Person實體類,未實現序列化接口,無父類*/
    FileOutputStream fo = new FileOutputStream("person.txt");
    ObjectOutputStream ss = new ObjectOutputStream(fo);
    Person person = new Person();
    person.setAge(100);
    person.setGender("性別");
    person.setHeight(165);
    person.setName("人類");
    ss.writeObject(person);
    ss.flush();
    ss.close();

    //反序列化
    FileInputStream sfis = new FileInputStream("person.txt");
    ObjectInputStream sois = new ObjectInputStream(sfis);
    Person ps = (Person) sois.readObject();
    System.out.println(ps.getName() +" "+ps.getGender()+" "+ps.getHeight() +" "+ps.getAge());

    /**結果:
        在執(zhí)行writeObject(person)是發(fā)生異常
    Exception in thread "main" java.io.NotSerializableException:
        com.springboot.SpringBootDemo.serializable.Person
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.writeObject(Unknown Source)
        at com.springboot.SpringBootDemo.serializable.SubTypeSerializable.main(SubTypeSerializable.java:21)
     Exception in thread "main" java.io.WriteAbortedException: writing aborted;    java.io.NotSerializableException: com.springboot.SpringBootDemo.serializable.Person
        at java.io.ObjectInputStream.readObject0(Unknown Source)
        at java.io.ObjectInputStream.readObject(Unknown Source)
        at com.springboot.SpringBootDemo.serializable.SubTypeSerializable.main(SubTypeSerializable.java:28)
     * */
    System.out.println("<--------------------------------------------------------------------------->");

    /**(二)、Male繼承父類Person,自身實現序列化接口,其父類Person沒有實現序列化接口*/
    FileOutputStream fos = new FileOutputStream("male.txt");
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    Male male = new Male();
    /**
     * 其父類的父類Person的屬性
     * 
     * public String name;
       public String gender;
       public int age;
       float height;
     * */
    male.setName("張三");
    male.setGender("男性");
    male.setAge(25);
    male.setHeight(175);
    /**
     * 其自身屬性
     * public boolean beard;
     * */
    male.setBeard(true);
    oos.writeObject(male);
    oos.flush();
    oos.close();

    //反序列化
    FileInputStream fis = new FileInputStream("male.txt");
    ObjectInputStream ois = new ObjectInputStream(fis);
    Male ml = (Male) ois.readObject();
    System.out.println(ml.getName() +" "+ml.getGender()+" "+ml.getHeight() +" "+ml.getAge()+" "+male.isBeard());

    /**結果:
     * 父類沒有被序列化,唯獨子類屬性被序列化
     * 
     * ml.getName() == null 
     * ml.getGender() == null 
     * ml.getHeight() == 0.0 
     * ml.getAge() == 0 
     * male.isBeard() == true
     * 父類屬性:
     *  public String name;
        public String gender;
        public int age;
        float height;
                   均未實現序列化;
                自身屬性:
        public boolean beard;
                    實現序列化        
     * */

    System.out.println("<--------------------------------------------------------------------------->");

    /**(三)、Female實現序列化接口,無父類*/
    FileOutputStream ffos = new FileOutputStream("female.txt");
    ObjectOutputStream foos = new ObjectOutputStream(ffos);
    Female female = new Female();
    /**
     * 其自身的屬性
     * public String name;
       public String gender;
       public int age;
       float height;
     **/
    female.setAge(25);
    female.setGender("女性");
    female.setHeight(165);
    female.setName("張芳");
    foos.writeObject(female);
    foos.flush();
    foos.close();

    //反序列化
    FileInputStream ffis = new FileInputStream("female.txt");
    ObjectInputStream fois = new ObjectInputStream(ffis);
    Female fm = (Female) fois.readObject();
    System.out.println(fm.getName() +" "+fm.getGender()+" "+fm.getHeight() +" "+fm.getAge());

    /**結果:
     * 自身屬性均實現序列化
     * 
     * fm.getName() == 張芳
     * fm.getGender() == 女性 
     * fm.getHeight() == 165.0 
     * fm.getAge() == 25
     * 所有屬性均實現序列化*/
    System.out.println("<--------------------------------------------------------------------------->");

    /**(四)、Students未實現序列化接口,繼承父類Male,其父類繼承父類Person,自身實現序列化接口,其父類Person沒有實現序列化接口*/
    FileOutputStream stufos = new FileOutputStream("students.txt");
    ObjectOutputStream stuoos = new ObjectOutputStream(stufos);
    Students students = new Students();
    /**
     * 其父類的父類Person的屬性
     * 
     * public String name;
       public String gender;
       public int age;
       float height;
     * */
    students.setName("王小明");
    students.setGender("男性");
    students.setAge(15);
    students.setHeight(160);
    /**
     * 其父類Male屬性
     * public boolean beard;
     * */
    students.setBeard(true);
    /**
     * 自身屬性
     * public String stuCard;
       private int grades;
     * */
    students.setStuCard("1234567890987");
    students.setGrades(300);
    stuoos.writeObject(students);
    stuoos.flush();
    stuoos.close();

    //反序列化
    FileInputStream stufis = new FileInputStream("students.txt");
    ObjectInputStream stuois = new ObjectInputStream(stufis);
    Students st = (Students) stuois.readObject();
    System.out.println(st.getName() +" "+st.getGender()+" "+st.getAge()+" "+st.getHeight()+" "+st.isBeard()+" "+st.getStuCard()+" "+st.getGrades());

    /**結果:
     * 父類的父類屬性未實現序列化,父類實現序列化,自身實現序列化
     * st.getName() == null 
     * st.getGender() == null 
     * st.getAge() == 0 
     * st.getHeight() == 0.0 
     * st.isBeard() == true 
     * st.getStuCard() == 1234567890987 
     * st.getGrades() == 300
     * 自身public String stuCard;
          private int grades;
            實現序列化;
           而父類Male屬性
          public boolean beard
            實現序列化;
           父類的父類Person
        public String name;
        public String gender;
        public int age;
        float height;
                   未實現序列化
     * */
    }
}
### 6.3 回顧

1)在使用ObjectInputStream、ObjectOutputStream對對象進行寫入寫出時,其寫入的對象的類需要實現java.io.Serializable序列化接口,否則會報出 writeObject()異常:
        Exception in thread "main" java.io.NotSerializableException: com.springboot.SpringBootDemo.serializable.Person
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.writeObject(Unknown Source)
        at com.springboot.SpringBootDemo.serializable.SubTypeSerializable.main(SubTypeSerializable.java:21)
        readObject()異常: 
        Exception in thread "main" java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: com.springboot.SpringBootDemo.serializable.Person
            java.io.ObjectInputStream.readObject0(Unknown Source)
            at java.io.ObjectInputStream.readObject(Unknown Source)
            at com.springboot.SpringBootDemo.serializable.SubTypeSerializable.main(SubTypeSerializable.java:28)
2)父類未實現java.io.Serializable序列化接口,其子類依然可以進行序列化,但其子類進行對象序列化讀寫時,父類無法被序列化,只能自身實現序列化;

3)自身實現java.io.Serializable序列化接口,在進行對象讀寫時會被實現序列化;

4)父類實現java.io.Serializable序列化接口,其子類不需要再次申明實現序列化,子類在進行對象序列化讀寫時,父類和子類均被實現序列化。

### 7.1 總結

#### 1)java.io.Serializable接口

首先,Serializable類是一個接口,所以對象的序列化并不是Serializable來實現的;

其次,Serializable是一個標簽,各種序列化類在讀取到這個標簽的時候,會按照自己的方式進行序列化。

#### 2)序列化是干什么的,為什么需要序列化

我們知道,當兩個進程進行遠程通信時,可以相互發(fā)送各種類型的數據,包括文本、圖片、音頻、視頻等,而這些數據都會以二進制序列的形式在網絡上傳送。

那么當兩個Java進程進行通信時,能否實現進程間的對象傳送呢?答案是可以的!如何做到呢?這就需要Java序列化與反序列化了!

換句話說:一方面,發(fā)送方需要把這個Java對象轉換為字節(jié)序列,然后在網絡上傳送;另一方面,接收方需要從字節(jié)序列中恢復出Java對象。

當我們明晰了為什么需要Java序列化和反序列化后,我們很自然地會想Java序列化的好處。

- 實現了數據的持久化,通過序列化可以記錄下數據結構或者對象的狀態(tài)(也就是實體變量Object[注:不是Class]某一個時間點的值),把數據臨時或者永久地保存到硬盤上(通常存放在文件里);
- 利用序列化實現遠程通信,并且在數據傳遞過程中或者使用時能夠保證數據結構或者對象狀態(tài)的完整性和可傳遞性,即在網絡上傳送對象的字節(jié)序列。

以上就是java實現序列化與反序列化的知識匯總,內容較為全面,小編相信有部分知識點可能是我們日常工作可能會見到或用到的。希望你能通過這篇文章學到更多知識。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI