序列化代理模式

實(shí)現(xiàn)java.io.Serializable接口, 會增加出錯(cuò)和出現(xiàn)安全問題的可能性, 因?yàn)樗_放了實(shí)例的另一種來源 ---- 反序列化. 有一種方法可以減少風(fēng)險(xiǎn), 那就是序列化代理模式.

類圖如下:

序列化代理模式.png

小二, 上代碼 !

import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * 1. 序列化Person時(shí), 會調(diào)用調(diào)用writeReplace()生成一個(gè)PersonProxy對象, 然后對此對象進(jìn)行序列化 (不是對Person類對象進(jìn)行序列化,
 *      由序列化文件的內(nèi)容可以得知, 可以查看序列化生成的文件, 文件中內(nèi)容為如下圖 (代碼之后的圖)
 * 2. 反序列化時(shí), 會調(diào)用PersonProxy的readResolve()方法生成一個(gè)Person對象, 
 *      最后返回此對象的拷貝 (通過PersonProxy類的readResolve方法和main方法中的輸出可以看出)
 * 3. 因此, Person類的序列化工作完全交給PersonProxy類, 正如此模式的名稱所表達(dá)的一樣
 */
public class Person implements Serializable {
    private final String name;
    private final String hobby;
    private final int age;

    public Person(String name, String hobby, int age) {
        System.out.println("Person(String name, String hobby, int age)");

        //約束條件
        if(age < 0 || age > 200) {
            throw new IllegalArgumentException("非法年齡");
        }

        this.name = name;
        this.hobby = hobby;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public String getHobby() {
        return hobby;
    }

    public int getAge() {
        return age;
    }


    private static class PersonProxy implements Serializable {
        private final String name;
        private final String hobby;
        private final int age;

        public PersonProxy(Person original) {
            System.out.println("PersonProxy(Person original)");
            this.name = original.getName();
            this.hobby = original.getHobby();
            this.age = original.getAge();
        }

        private Object readResolve() {
            System.out.println("PersonProxy.readResolve()");
            Person person = new Person(name, hobby, age);
            System.out.println("resolveObject: " + person);
            return person;
        }
    }

    private Object writeReplace() {
        System.out.println("Person.writeReplace()");
        return new PersonProxy(this); //readObject的時(shí)候是調(diào)用, PersonProxy的readResolve()
    }

    //此方法不會執(zhí)行,
    private void writeObject(ObjectOutputStream out) {
        System.out.println("Person.writeObject()");
    }

    //防止攻擊者偽造數(shù)據(jù), 企圖違反約束條件 (如: 違反年齡約束)
    private Object readObject(ObjectInputStream in) throws InvalidObjectException {
        System.out.println("Person.readObject()");
        throw new InvalidObjectException("Proxy required");
    }
}

序列化文件中的內(nèi)容:

序列化文件內(nèi)容

客戶程序如下:

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

public class Main {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        Person person = new Person("張三", "足球" ,25);
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("serial.ser"));
        out.writeObject(person);
        out.flush();
        out.close();

        Thread.sleep(1000);

        ObjectInputStream in = new ObjectInputStream(new FileInputStream("serial.ser"));
        Person deserPerson = (Person) in.readObject();
        System.out.println("main: " + person);
        in.close();

        if(person == deserPerson) {
            System.out.println("序列化前后是同一個(gè)對象");
        } else {
            //程序會走這一段, 反序列化會創(chuàng)建對象, 但是不會執(zhí)行類的構(gòu)造方法, 而是使用輸入流構(gòu)造對象
            System.out.println("序列化前后不是同一個(gè)對象, 哈哈哈");
        }
    }
}

此模式工作原理如下:

  1. 序列化Person時(shí), 會調(diào)用調(diào)用writeReplace()生成一個(gè)PersonProxy對象, 然后對此對象進(jìn)行序列化 (不是對Person類對象進(jìn)行序列化, 由序列化文件的內(nèi)容可以得知, 可以查看序列化生成的文件, 文件中內(nèi)容為如下圖
序列化文件中的內(nèi)容
  1. 反序列化時(shí), 會調(diào)用PersonProxy的readResolve()方法生成一個(gè)Person對象, 最后返回此對象的拷貝 (通過PersonProxy類的readResolve方法和main方法中的輸出可以看出)
  2. 因此, Person類的序列化工作完全交給PersonProxy類, 正如此模式的名稱所表達(dá)的一樣

本文參考: 《Effective Java 第2版》第78條 『考慮使用序列化代理代替序列化實(shí)例』

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • JAVA序列化機(jī)制的深入研究 對象序列化的最主要的用處就是在傳遞,和保存對象(object)的時(shí)候,保證對象的完整...
    時(shí)待吾閱讀 11,166評論 0 24
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評論 19 139
  • 官方文檔理解 要使類的成員變量可以序列化和反序列化,必須實(shí)現(xiàn)Serializable接口。任何可序列化類的子類都是...
    獅_子歌歌閱讀 2,546評論 1 3
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,626評論 18 399
  • 終于入冬了~發(fā)現(xiàn)上個(gè)冬季我是裸奔的~
    Joanachen閱讀 246評論 0 2

友情鏈接更多精彩內(nèi)容