天天看點

Java 中的 Serilizable 接口和 transient 關鍵字Java 中的 Serilizable 接口和 transient 關鍵字

Java 中的 Serilizable 接口和 transient 關鍵字

Serilizable 接口

Serilizable 接口是一個辨別接口,沒有方法和變量,jdk 中的描述為:實作了該接口的類具有可序列化性。所謂可序列化性即對象可以被序列化成位元組碼,用于存儲到磁盤或者進行網絡傳輸,同時又可以從位元組碼反序列化成對象。

父類實作了 Serilizable 接口,其子類也是可序列化的。

如果類實作了 Serilizable 接口 ,但是其一個引用成員變量沒有實作 Serilizable 接口将抛出 NotSerializableException 異常。

下面是序列化和反序列化的一些 Demo:

public class TestWrite {

    public static void main(String[] args) {
        try {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Object.txt"));
            Demo demo = new Demo(1,"1");
            oos.writeObject(demo);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }


}

class TestRead {

    public static void main(String[] args) {

        try {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Object.txt"));
            Demo demo = (Demo)ois.readObject();
            System.out.println(demo.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

class Demo implements Serializable {

    private int var1;
    private String var2;

    public Demo(int var1, String var2) {
        this.var1 = var1;
        this.var2 = var2;
    }

    @Override
    public String toString() {
        return "Demo["+var2+","+var2+"]";
    }
}
           

當我們再執行反序列化前,即執行上面代碼 TestRead 類的 main 方法前,調用修改了 Demo 類,會導緻反序列化抛出異常:

class Demo implements Serializable {

    private int var1;
    private String var2;
    private String var3;

    public Demo(int var1, String var2) {
        System.out.println('o');
        this.var1 = var1;
        this.var2 = var2;
    }

    @Override
    public String toString() {
        return "Demo["+var2+","+var2+"]";
    }
}

___________________________________________

java.io.InvalidClassException: dataStructure.test.Demo; local class incompatible: stream classdesc serialVersionUID = -8932904352177221623, local class serialVersionUID = 6808531953279345133
           

異常為 serialVersionUID 不一緻導緻,即序列化前的對象和和目前對象版本不一緻。關于 serialVersionUID 将在後面講到。

transient 關鍵字

當我們實作 serilizable 接口,但是又不想将類中部分變量序列化,就可以在變量前加上 transient 關鍵字,保證其不會被序列化,當然 transient 發揮的作用會被一些自定義序列化方法破壞。

class Demo implements Serializable {

    private int var1;
    private transient String var2;

    public Demo(int var1, String var2) {
        this.var1 = var1;
        this.var2 = var2;
    }

    public String toString() {
        return "Demo["+var1+","+var2+"]";
    }
}

___________________________________________

Demo[1,null]

           

自定義序列化

上面說到自定義序列化方法即:

private void writeObject(java.io.ObjectOutputStream out) throws IOException;
private void readObject(java.io.ObjectIutputStream in) throws IOException,ClassNotFoundException;
private Object writeReplace() throws ObjectStreamException 
private Object readResolve() throws ObjectStreamException
private void readObjectNoData() throws ObjectStreamException;
           

通過readObject和writeOjbect方法可以幹預序列化,例如把待序列化的對象的某個屬性字段加密和解密,或者把transient關鍵字标記的屬性序列化寫出去。

class Demo implements Serializable {

    private int var1;
    private transient String var2;

    public Demo(int var1, String var2) {
        this.var1 = var1;
        this.var2 = var2;
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeInt(var1 +10);
    }

    private void readObject(ObjectInputStream in) throws IOException,
            ClassNotFoundException {
        in.defaultReadObject();
        var1 = in.readInt() + 10;
       
    }

    public String toString() {
        return "Demo["+var1+","+var2+"]";
    }
}
___________________________________________

Demo[21,null]

           
writeReplace方法會在writeObject方法之前執行。 ObjectOutputStream會把writeReplace方法傳回的對象序列化寫出去
readResolve方法會在readObject方法之後執行,可以再次修改readObject方法傳回的對象資料。
當序列化流不完整時,readObjectNoData()方法可以用來正确地初始化反序列化的對象。

serialVersionUID

serialVersionUID 是 Java 為實作 Serilizable 接口的類産生的版本辨別,確定反序列化時發生方和接收方是相容的。如果序列化前後 serialVersionUID 不一緻則會抛出 InvalidClassException 異常。建議顯示定義 serialVersionUID 的值, JDK 版本不一緻或者 IDE 重新編譯使自動生成的 serialVersionUID 不一緻,進而導緻反序列化錯誤。

上面因為新增一個變量導緻抛出異常可以通過顯示定義 serialVersionUID 的 方法實作:

class Demo implements Serializable {

    private final static long serialVersionUID = 1L;

    private int var1;
    private String var2;
    private String var3;

    public Demo(int var1, String var2) {
        this.var1 = var1;
        this.var2 = var2;
    }

    public String toString() {
        return "Demo["+var1+","+var2+"]";
    }
}
____________________________________________

Demo[1,1]
           

Externalizable 接口

Externalizable 接口不同于 Serializable 接口,實作此接口必須實作接口中的兩個方法實作自定義序列化,這是強制性的;特别之處是必須提供pulic的無參構造器,因為在反序列化的時候需要反射建立對象。
class Demo implements Externalizable {

    private final static long serialVersionUID = 1L;

    private int var1;
    private String var2;

    public Demo(int var1, String var2) {
        this.var1 = var1;
        this.var2 = var2;
    }

    public Demo() {
    }

    public String toString() {
        return "Demo["+var1+","+var2+"]";
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        StringBuffer reverse = new StringBuffer(var2).reverse();
        out.writeInt(var1);
        out.writeObject(reverse);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        this.var1 = in.readInt();
        this.var2 = ((StringBuffer) in.readObject()).reverse().toString();
    }
}