• 《Effective Java 第三版》——第十二章 序列化


    《Effective Java 第三版》——第二章 创建和销毁对象

    《Effective Java 第三版》——第三章 所有对象都通用的方法

    《Effective Java 第三版》——第四章 类和接口 

    《Effective Java 第三版》——第五章 泛型

     《Effective Java 第三版》——第六章 枚举和注解

     《Effective Java 第三版》——第七章 Lambda 和 Stream 

     《Effective Java 第三版》——第八章 方法

    《Effective Java 第三版》——第九章 通用编程

    《Effective Java 第三版》——第十章 异常

     《Effective Java 第三版》——第十一章 并发

     《Effective Java 第三版》——第十二章 序列化

     

     反序列化炸弹

    调试程序过程中发现:root 的直接子元素不变,但是变量的赋值过程导致不断往子元素上添加节点,形成  2^100 炸弹

    package effectivejava.chapter12.item85;
    import static effectivejava.chapter12.Util.*;
    
    import java.util.HashSet;
    import java.util.Set;
    
    // Deserialization bomb - deserializing this stream takes forever - Page 340
    public class DeserializationBomb {
        public static void main(String[] args) throws Exception {
            System.out.println(bomb().length);
            deserialize(bomb());
        }
    
        static byte[] bomb() {
            Set<Object> root = new HashSet<>();
            Set<Object> s1 = root;
            Set<Object> s2 = new HashSet<>();
            for (int i = 0; i < 100; i++) {
                Set<Object> t1 = new HashSet<>();
                Set<Object> t2 = new HashSet<>();
                t1.add("foo"+i); // make it not equal to t2
                s1.add(t1);
                s1.add(t2);
                s2.add(t1);
                s2.add(t2);
                s1 = t1;
                s2 = t2;
            }
            return serialize(root);
        }
    }

    http://www.qmwxb.com/article/2280136.html

    有图示

     

     注意区分:逻辑数据 & 物理表示法

     

    package effectivejava.chapter12.item87;
    import java.io.*;
    
    // StringList with a reasonable custom serialized form  - Page 349
    public final class StringList implements Serializable {
        private transient int size   = 0;
        private transient Entry head = null;
    
        // No longer Serializable!
        private static class Entry {
            String data;
            Entry  next;
            Entry  previous;
        }
    
        // Appends the specified string to the list
        public final void add(String s) {  }
    
        /**
         * Serialize this {@code StringList} instance.
         *
         * @serialData The size of the list (the number of strings
         * it contains) is emitted ({@code int}), followed by all of
         * its elements (each a {@code String}), in the proper
         * sequence.
         */
        private void writeObject(ObjectOutputStream s)
                throws IOException {
            s.defaultWriteObject();
            s.writeInt(size);
    
            // Write out all elements in the proper order.
            for (Entry e = head; e != null; e = e.next)
                s.writeObject(e.data);
        }
    
        private void readObject(ObjectInputStream s)
                throws IOException, ClassNotFoundException {
            s.defaultReadObject();
            int numElements = s.readInt();
    
            // Read in all elements and insert them in list
            for (int i = 0; i < numElements; i++)
                add((String) s.readObject());
        }
    
        // Remainder omitted
    }

     

     

     

    package effectivejava.chapter12.item89.enumsingleton;
    
    import java.util.*;
    
    // Enum singleton - the preferred approach - Page 311
    public enum Elvis {
        INSTANCE;
        private String[] favoriteSongs =
            { "Hound Dog", "Heartbreak Hotel" };
        public void printFavorites() {
            System.out.println(Arrays.toString(favoriteSongs));
        }
    }

    enum 的源码逃不过的。。。

     

     

    package effectivejava.chapter12.item90;
    
    // Period class with serialization proxy - Pages 363-364
    
    import java.util.*;
    import java.io.*;
    
    // Immutable class that uses defensive copying
    public final class Period implements Serializable {
        private final Date start;
        private final Date end;
    
        /**
         * @param  start the beginning of the period
         * @param  end the end of the period; must not precede start
         * @throws IllegalArgumentException if start is after end
         * @throws NullPointerException if start or end is null
         */
        public Period(Date start, Date end) {
            this.start = new Date(start.getTime());
            this.end   = new Date(end.getTime());
            if (this.start.compareTo(this.end) > 0)
                throw new IllegalArgumentException(
                        start + " after " + end);
        }
    
        public Date start () { return new Date(start.getTime()); }
    
        public Date end () { return new Date(end.getTime()); }
    
        public String toString() { return start + " - " + end; }
    
    
        // Serialization proxy for Period class
        private static class SerializationProxy implements Serializable {
            private final Date start;
            private final Date end;
    
            SerializationProxy(Period p) {
                this.start = p.start;
                this.end = p.end;
            }
    
            private static final long serialVersionUID =
                    234098243823485285L; // Any number will do (Item 87)
        }
    
        // writeReplace method for the serialization proxy pattern
        private Object writeReplace() {
            return new SerializationProxy(this);
        }
    
        // readObject method for the serialization proxy pattern
        private void readObject(ObjectInputStream stream)
                throws InvalidObjectException {
            throw new InvalidObjectException("Proxy required");
        }
    }
    package effectivejava.chapter12;
    
    import java.io.*;
    
    public class Util {
        public static byte[] serialize(Object o) {
            ByteArrayOutputStream ba = new ByteArrayOutputStream();
            try {
                new ObjectOutputStream(ba).writeObject(o);
            } catch (IOException e) {
                throw new IllegalArgumentException(e);
            }
            return ba.toByteArray();
        }
    
        public static Object deserialize(byte[] bytes) {
            try {
                return new ObjectInputStream(
                        new ByteArrayInputStream(bytes)).readObject();
            } catch (IOException | ClassNotFoundException e) {
                throw new IllegalArgumentException(e);
            }
        }
    }
    package effectivejava.chapter12.item90;
    
    import java.io.*;
    
    /**
     * https://blog.csdn.net/Lirx_Tech/article/details/51303966
     * [疯狂Java]I/O:其它自定义序列化的方法(transient、writeReplace、readResolve、Externalizable)
     */
    
    /**
     * 1. 序列化Person时, 会调用调用writeReplace()生成一个PersonProxy对象, 然后对此对象进行序列化 (不是对Person类对象进行序列化,
     *      由序列化文件的内容可以得知, 可以查看序列化生成的文件, 文件中内容为如下图 (代码之后的图)
     *      ��sr1effectivejava.chapter12.item90.Person$PersonProxy_7R&Z��[IageLhobbytLjava/lang/String;Lnameq~xpt足球t张三
     * 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()
        }
    
        //此方法不会执行,为何?
        //实现writeReplace就不要实现writeObject了,因为writeReplace的返回值会被自动写入输出流中,
        // 就相当于自动这样调用:writeObject(writeReplace());
        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");
        }
    
        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("序列化前后不是同一个对象, 哈哈哈");
            }
        }
    }
    
    /**
     * /Library/Java/JavaVirtualMachines/jdk-13.0.2.jdk/Contents/Home/bin/java -Dfile.encoding=UTF-8 -classpath /Users/didi/git/effective-java-3e-source-code/bin effectivejava.chapter12.item90.Person
     * Person(String name, String hobby, int age)
     * Person.writeReplace()
     * PersonProxy(Person original)
     * PersonProxy.readResolve()
     * Person(String name, String hobby, int age)
     * main: effectivejava.chapter12.item90.Person@3f99bd52
     * 序列化前后不是同一个对象, 哈哈哈
     *
     * Process finished with exit code 0
      */
        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("序列化前后不是同一个对象, 哈哈哈");
            }
        }

    
    
    
    out.writeObject(person);
    
    会进入:
        private Object writeReplace() {
            System.out.println("Person.writeReplace()");
            return new PersonProxy(this); //readObject的时候是调用, PersonProxy的readResolve()
        }
    
    
    Person deserPerson = (Person) in.readObject();
    
    会进入:
            //反序列化时将序列化代理转变回为外围类的实例
            private Object readResolve() {
                System.out.println("PersonProxy.readResolve()");
                Person person = new Person(name, hobby, age);
                System.out.println("resolveObject: " + person);
                return person;
            }
  • 相关阅读:
    shiro注解,初始化资源和权限,会话管理
    shiro标签
    20个为前端开发者准备的文档和指南
    Canvas处理头像上传
    Chrome 实用调试技巧
    JS 代码编一个倒时器
    sql server优化
    在线图片压缩网站
    Request.QueryString
    C#网络爬虫
  • 原文地址:https://www.cnblogs.com/cx2016/p/13278197.html
Copyright © 2020-2023  润新知