Java:序列化和反序列化

2017-11-14  本文已影响0人  李眼镜

1.背景

某天,我在写代码定义 bean 的时候,顺手写了个 public class User implements Serializable,旁边的小哥哥看到了问我:你为什么要实现 Serializable 接口?你哪里用到它了吗?不实现这个接口可以吗?

emmm,皱眉沉思一下,好像也可以?

好吧,那先来了解一下 Serializable 接口涉及到的相关概念。

2.序列化协议+序列化和反序列化

自问自答

  • 问:序列化的目的是什么?
  • 答:方便的进行数据的交换和传输工作。

3.JDK类库中的序列化API

Java本身提供了对数据/对象序列化的支持。

3.1 对象序列化到文件

// User.java
package com.ann.javas.javacores.serialization.demo1;

import java.io.Serializable;


public class User implements Serializable{

    private static String HH="我是静态变量,我不会被序列化";
    private int userId;
    private String userName;
    private String address;

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public static String getHH() {
        return HH;
    }

    public static void setHH(String HH) {
        User.HH = HH;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

// Client.java
package com.ann.javas.javacores.serialization.demo1;

import java.io.*;

public class Client {

    public static void main(String[] args) throws Exception {
        toFile();
        fromFile();
    }


    // Object -> 文件
    public static void toFile() throws Exception {
        User user = new User();
        user.setUserId(1223);
        user.setUserName("令习习习");
        user.setAddress("北京");

        System.out.println("对象:"+user.toString());
        System.out.println("对象中的静态变量:"+user.getHH());
        ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("./out.txt")));
        oo.writeObject(user);
        System.out.println("序列化成功");
        oo.close();
    }

    // 文件 -> Object
    public static void fromFile() throws Exception{
        User tmp = new User();
        tmp.setHH("我是静态变量,我的值是存在JVM静态存储区的,不是反序列化来的");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("./out.txt")));
        System.out.println("反序列化成功");
        User user = (User) ois.readObject();
        System.out.println("对象:"+user.toString());
        System.out.println("对象中的静态变量:"+user.getHH());
        ois.close();
    }
}

运行结果:

对象:User{userId=1223, userName='令习习习', address='北京'}
对象中的静态变量:我是静态变量,我不会被序列化
序列化成功
反序列化成功
对象:User{userId=1223, userName='令习习习', address='北京'}
对象中的静态变量:我是静态变量,我的值是存在JVM静态存储区的,不是反序列化来的

3.2 隐藏指定字段

在某些场景下,你希望某些字段不要被序列化,此时可以使用 transient 关键字来进行排除。

// User.java
package com.ann.javas.javacores.serialization.demo2;

import java.io.Serializable;


public class User implements Serializable{

    private int userId;
    private String userName;
    private transient String address;

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}


//Client.java
package com.ann.javas.javacores.serialization.demo2;

import java.io.*;

public class Client {

    public static void main(String[] args) throws Exception {
        toFile();
        fromFile();
    }


    // Object -> 文件
    public static void toFile() throws Exception {
        User user = new User();
        user.setUserId(1223);
        user.setUserName("令习习习");
        user.setAddress("北京");

        System.out.println("对象:"+user.toString());
        ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("./out.txt")));
        oo.writeObject(user);
        System.out.println("序列化成功");
        oo.close();
    }

    // 文件 -> Object
    public static void fromFile() throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("./out.txt")));
        System.out.println("反序列化成功");
        User user = (User) ois.readObject();
        System.out.println("对象:"+user.toString());
        ois.close();
    }
}

运行结果:

对象:User{userId=1223, userName='令习习习', address='北京'}
序列化成功
反序列化成功
对象:User{userId=1223, userName='令习习习', address='null'}

这里使用 transient 修饰了 Useraddress 变量,因此address不会被序列化,也不会被反序列化。

自问自答

  • 问:使用 transient 修饰的变量,就一定不会被序列化了吗?
  • 答:不一定,要取决于你的程序是怎么写的。

3.3 Serializable 的 readObject 和 writeObject

// User.java
package com.ann.javas.javacores.serialization.demo3;

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


public class User implements Serializable{

    private int userId;
    private String userName;
    private transient String address;

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                ", address='" + address + '\'' +
                '}';
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeObject(address);
    }
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        address = (String)in.readObject();
    }
}

// Client.java
package com.ann.javas.javacores.serialization.demo3;

import java.io.*;

public class Client {

    public static void main(String[] args) throws Exception {
        toFile();
        fromFile();
    }


    // Object -> 文件
    public static void toFile() throws Exception {
        User user = new User();
        user.setUserId(1223);
        user.setUserName("令习习习");
        user.setAddress("北京");

        System.out.println("对象:"+user.toString());
        ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("./out.txt")));
        oo.writeObject(user);
        System.out.println("序列化成功");
        oo.close();
    }

    // 文件 -> Object
    public static void fromFile() throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("./out.txt")));
        System.out.println("反序列化成功");
        User user = (User) ois.readObject();
        System.out.println("对象:"+user.toString());
        ois.close();
    }
}


运行结果:

对象:User{userId=1223, userName='令习习习', address='北京'}
序列化成功
反序列化成功
对象:User{userId=1223, userName='令习习习', address='北京'}

在这个例子中,User 定义了两个private方法:readObject()writeObject()

writeObject() 方法中会先调用 ObjectOutputStream 中的 defaultWriteObject() 方法,该方法会执行默认的序列化机制,此时会忽略掉被 transient 修饰的address字段。然后再调用 writeObject() 方法显示地将address字段写入到 ObjectOutputStream 中。

readObject() 的作用则是针对对象的读取,其原理与 writeObject() 方法相同。

3.4 实现 Externalizable 接口

在Java中,对象的序列化可以通过实现两种接口来实现:

// User.java
package com.ann.javas.javacores.serialization.demo3;

import java.io.*;


public class User implements Externalizable{

    private int userId;
    private String userName;
    private transient String address;

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                ", address='" + address + '\'' +
                '}';
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeInt(userId + 122);
        out.writeObject(userName);
        out.writeObject(address);
        System.out.println("writeExternal:我没有存原文哦");
        out.flush();
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        userId = in.readInt();
        userName = (String)in.readObject();
        address = (String)in.readObject();
    }
}

// Client.java
package com.ann.javas.javacores.serialization.demo3;

import java.io.*;

public class Client {

    public static void main(String[] args) throws Exception {
        toFile();
        fromFile();
    }


    // Object -> 文件
    public static void toFile() throws Exception {
        User user = new User();
        user.setUserId(1223);
        user.setUserName("令习习习");
        user.setAddress("北京");

        System.out.println("对象:"+user.toString());
        ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("./out.txt")));
        oo.writeObject(user);
        System.out.println("序列化成功");
        oo.close();
    }

    // 文件 -> Object
    public static void fromFile() throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("./out.txt")));
        System.out.println("反序列化成功");
        User user = (User) ois.readObject();
        System.out.println("对象:"+user.toString());
        ois.close();
    }
}

运行结果:

对象:User{userId=1223, userName='令习习习', address='北京'}
writeExternal:我没有存原文哦
序列化成功
反序列化成功
对象:User{userId=1345, userName='令习习习', address='北京'}

这里有几个关键单需要说明:

4.关于 Serializable 和 Externalizable 的总结和附加说明

自问自答

  • 问: writeObject()readObject() 都是private方法,它们是怎么被调用的呢?
  • 答:很显然,反射。详情可见 ObjectOutputStream 中的 writeSerialData 方法,以及 ObjectInputStream 中的 readSerialData 方法。
上一篇 下一篇

猜你喜欢

热点阅读