Java反序列化

Java序列化原理

Java 的序列化与反序列化分别是由 ObjectOutputStream 类的 writeObject 方法和 ObejctInputStream 的 readObject 方法实现的

最简单的(反)序列化

Java 中并不是所有类都可以进行序列化的,如果要对类进行序列化的话,需要让类实现 java.io.serialize 这个接口

  1. 序列化

    一个简单的例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // Person.java
    public class Person implements serialze{
    private String name;

    public Person(String name){
    this.name = name;
    }

    public String toString(){
    return "Person("+this.name+")";
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // demo1.java
    public class demo1{
    public static void main(String[] args){
    Person p = new Person("yyp");
    try{
    FileOutputStream f = new FileOutputStream("1.txt");
    ObjectOutputStream ser = new ObejctOutputStream(f);
    ser.writeObject(p);
    ser.close();
    f.close();
    }catch(IOException i){
    i.printStackTrace();
    }catch(ClassNotFoundException c){
    c.printStackTrace();
    }
    }
    }

    可以看到在 writeObject() 方法上实现了类的序列化

  2. 反序列化

    直接看例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // demo2.java
    public class demo2{
    public static void main(String[] args){
    Person p = new Person("yyp");
    try{
    FIleInputStream f = new FileInputStream("1.txt");
    ObejctInputStream unser = new ObejectInputStream(f);
    Person pp = (Person)unser.readObject();
    System.out.println(pp);
    }catch(IOException i){
    i.printStackTrace();
    }catch(ClassNotFoundException c){
    c.printStackTrace();
    }
    }
    }

    在 readObject 处实现了反序列化

自定义(反)序列化

  1. transient

    序列化时会自动忽略带有 transient 属性

    对于上面的 Person 类,如果给 name 加上 transient 关键字 private transient String name; ,反序列化输出得到的结果为 Person(null),而没有加这个关键字时,得到的鸡国为 Person(yyp)。可以看到对于带有 transient 字段的属性会在序列化的时候赋予默认值。

  2. writeReplace 和 readResolve 方法

    writeReplace 和 readResolve 类似于 php 的 __sleep__ 和 __wakeup__ 方法,这两个方法使用时在需要被序列化的类中进行定义,如:

    1
    2
    3
    4
    5
    public Object writeReplace() throws ObjectStreamException {
    ArrayList arr = new ArrayList<>2;
    arr.add(this.name);
    return arr;
    }

    加上这个函数时,我们得到的反序列化结果为: [yyp],可以看到,writeObject 序列化时的对象为 writeReplace 函数返回的值

    再来看一下 readResolve 方法:

    1
    2
    3
    4
    public Object readResolve() throws ObjectStreamException {
    Person p = new Person("asdasd");
    return p;
    }

    重新序列化,查看反序列化结果,得到 Person(asdasd)

    综上,可以得到函数之间调用的顺序:writeReplace -> writeObject,readObject -> readResolve

  3. Externalizable 自定义序列化

    需要在类中实现 writeExternal 和 readExternal 方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class Person implements Externalizable {
    private String name;
    private int age;

    public void writeExternal(ObjectOutput out) throws IOException{
    // 可以对属性进行其他的操作
    out.writeObject(name);
    out.writeInt(age);
    }
    public void readExternam(ObejctInput in) throws IOExeption,classNotFoundException{
    name = (String) in.readObject();
    age = in.readInt();
    // ...
    }
    }
  4. resolveClass 方法

    resolveClass 方法是 ObjectInputStream 类中的一个方法,在 ObjectInputStream 中的 resolveClass 方法默认做的工作是检查这个类可否被 load(类的加载),但是可以通过重载 resolveClass,使其具有白名单功能。

Contents
  1. 1. Java序列化原理
    1. 1.1. 最简单的(反)序列化
    2. 1.2. 自定义(反)序列化
|