序列化代理模式

来源:互联网 发布:淘宝长图 编辑:程序博客网 时间:2024/05/22 15:49

实现java.io.Serializable接口, 会增加出错和出现安全问题的可能性, 因为它开放了实例的另一种来源 ---- 反序列化. 有一种方法可以减少风险, 那就是序列化代理模式.

类图如下:

序列化代理模式.png

小二, 上代码 !

import java.io.InvalidObjectException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;/** * 1. 序列化Person时, 会调用调用writeReplace()生成一个PersonProxy对象, 然后对此对象进行序列化 (不是对Person类对象进行序列化, *      由序列化文件的内容可以得知, 可以查看序列化生成的文件, 文件中内容为如下图 (代码之后的图) * 2. 反序列化时, 会调用PersonProxy的readResolve()方法生成一个Person对象,  *      最后返回此对象的拷贝 (通过PersonProxy类的readResolve方法和main方法中的输出可以看出) * 3. 因此, Person类的序列化工作完全交给PersonProxy类, 正如此模式的名称所表达的一样 */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的时候是调用, PersonProxy的readResolve()    }    //此方法不会执行,    private void writeObject(ObjectOutputStream out) {        System.out.println("Person.writeObject()");    }    //防止攻击者伪造数据, 企图违反约束条件 (如: 违反年龄约束)    private Object readObject(ObjectInputStream in) throws InvalidObjectException {        System.out.println("Person.readObject()");        throw new InvalidObjectException("Proxy required");    }}

序列化文件中的内容:

序列化文件内容

客户程序如下:

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("序列化前后是同一个对象");        } else {            //程序会走这一段, 反序列化会创建对象, 但是不会执行类的构造方法, 而是使用输入流构造对象            System.out.println("序列化前后不是同一个对象, 哈哈哈");        }    }}

此模式工作原理如下:

  1. 序列化Person时, 会调用调用writeReplace()生成一个PersonProxy对象, 然后对此对象进行序列化 (不是对Person类对象进行序列化, 由序列化文件的内容可以得知, 可以查看序列化生成的文件, 文件中内容为如下图
序列化文件中的内容
  1. 反序列化时, 会调用PersonProxy的readResolve()方法生成一个Person对象, 最后返回此对象的拷贝 (通过PersonProxy类的readResolve方法和main方法中的输出可以看出)
  2. 因此, Person类的序列化工作完全交给PersonProxy类, 正如此模式的名称所表达的一样

本文参考: 《Effective Java 第2版》第78条 『考虑使用序列化代理代替序列化实例』



作者:元亨利贞o
链接:http://www.jianshu.com/p/a3b3dd80c8d6
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
原创粉丝点击