Java反序列化基础

技术渣 2020年02月21日

在网上看到自己复习学过但不熟练的反序列化基础笔记,抄录并学习。

Java的序列化和反序列化

Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。把字节序列恢复为对象的过程称为对象的反序列化。

  • 序列化就是把对象的状态信息转换为字节序列(即可以存储或传输的形式)过程
  • 反序列化即逆过程,由字节流还原成对象

位置: Java.io.ObjectOutputStream   java.io.ObjectInputStream
序列化:  ObjectOutputStream --> writeObject()该方法对参数指定的obj对象进行序列化,把字节序列写到一个目标输出流中,按Java的标准约定是给文件一个.ser扩展名。

反序列化: ObjectInputStream --> readObject() 该方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。

注:实现Serializable和Externalizable接口的类的对象才能被序列化。

并不是一个实现了序列化接口的类的所有字段及属性,都是可以序列化的:

  • 如果该类有父类,则分两种情况来考虑:如果该父类已经实现了可序列化接口,则其父类的相应字段及属性的处理和该类相同;如果该类的父类没有实现可序列化接口,则该类的父类所有的字段属性将不会序列化,并且反序列化时会调用父类的默认构造函数来初始化父类的属性,而子类却不调用默认构造函数,而是直接从流中恢复属性的值。
  • 如果该类的某个属性标识为static类型的,则该属性不能序列化。
  • 如果该类的某个属性采用transient关键字标识,则该属性不能序列化。

Demo

一般方式

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.text.MessageFormat;
import java.io.Serializable;

class Person implements Serializable {

    /**
     * 序列化ID
     */
    private static final long serialVersionUID = -5809782578272943999L;


    private int age;
    private String name;
    private String sex;

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    public String getSex() {
        return sex;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}

/**
 * ClassName: SerializeAndDeserialize
 * Description: 测试对象的序列化和反序列
 */
public class SerializeDeserialize_readObject {

    public static void main(String[] args) throws Exception {
        SerializePerson();//序列化Person对象
        Person p = DeserializePerson();//反序列Perons对象
        System.out.println(MessageFormat.format("name={0},age={1},sex={2}",
                                                 p.getName(), p.getAge(), p.getSex()));
    }

    /**
     * MethodName: SerializePerson
     * Description: 序列化Person对象
     */
    private static void SerializePerson() throws FileNotFoundException,
            IOException {
        Person person = new Person();
        person.setName("ssooking");
        person.setAge(20);
        person.setSex("男");
        // ObjectOutputStream 对象输出流,将Person对象存储到Person.txt文件中,完成对Person对象的序列化操作
        ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
                new File("Person.txt")));
        oo.writeObject(person);
        System.out.println("Person对象序列化成功!");
        oo.close();
    }

    /**
     * MethodName: DeserializePerson
     * Description: 反序列Perons对象
     */
    private static Person DeserializePerson() throws Exception, IOException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("Person.txt")));
        /*
            FileInputStream fis = new FileInputStream("Person.txt"); 
            ObjectInputStream ois = new ObjectInputStream(fis);
        */
        Person person = (Person) ois.readObject();
        System.out.println("Person对象反序列化成功!");
        return person;
    }

}

自定义序列化的方式

public class SeriDemo1 implements Serializable {
    private String name;
    transient private String password;
    // 瞬态,不可序列化状态,该字段的生命周期仅存于调用者的内存中
    public SeriDemo1() {
    }
    public SeriDemo1(String name, String password) {
        this.name = name;
        this.password = password;
    }
    //模拟对密码进行加密
    private String change(String password) {
        return password + "minna";
    }
    //写入
    private void writeObject(ObjectOutputStream outStream) throws IOException {
        outStream.defaultWriteObject();
        outStream.writeObject(change(password));
    }
    //读取
    private void readObject(ObjectInputStream inStream) throws IOException,
        ClassNotFoundException {
        inStream.defaultReadObject();
        String strPassowrd = (String) inStream.readObject();
        //模拟对密码解密
        password = strPassowrd.substring(0, strPassowrd.length() - 5);
    }
    //返回一个“以文本方式表示”此对象的字符串
    public String toString() {
        return "SeriDemo1 [name=" + name + ", password=" + password + "]";
    }
    //静态的main
    public static void main(String[] args) throws Exception {
        SeriDemo1 demo = new SeriDemo1("haom", "0123");
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(buf);
        out.writeObject(demo);
        ObjectInputStream in = new ObjectInputStream(new
            ByteArrayInputStream(buf.toByteArray()));
        demo = (SeriDemo1) in.readObject();
        System.out.println(demo);
    }
}

Referer

Java反序列化基础