【JAVA】序列化与反序列化

2017-10-23  本文已影响0人  Pino_HD

0x01 概述

什么是序列化,简单的来说,序列化就是为了保存对象的状态;而反序列化就是把保存的对象状态再读出来。

使用场景:

  1. 当想把内存中的对象状态保存到一个文件或者数据库中的时候
  2. 当想用套接字在网络上传送对象的时候
  3. 当想通过RMI传输对象的时候

0x02 Java支持序列化种类

Java支持的序列化有三种

  1. 自定义实现Serializable接口的类
  2. Java的基本类型
  3. Java自带的实现了Serializable接口的类

下面用程序展示着三种情况

支持自定义实现Serializable接口的类:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;


public class SerialTest1 {

    private static final String TMP_FILE = ".serialtest1.txt";

    public static void main (String args[]) {

        testWrite();
        testRead();
    }

    private static void testWrite() {

        try {

            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(TMP_FILE));
            Box box = new Box("desk", 80, 48);
            out.writeObject(box);
            System.out.println("testWrite box:" + box);
            out.close();
        }catch (Exception ex) {

            ex.printStackTrace();
        }
    }

    private static void testRead() {

        try {

            ObjectInputStream in = new ObjectInputStream(new FileInputStream(TMP_FILE));
            Box box = (Box)in.readObject();
            System.out.println("testRead box:" + box);
            in.close();
        }catch (Exception e) {

            e.printStackTrace();
        }
    }
}

class Box implements Serializable {

    private int width;
    private int height;
    private String name;

    public Box (String name, int width, int height) {

        this.name = name;
        this.height = height;
        this.width = width;
    }

    public String toString () {
        return "[" + name + ": (" + width + ", " + height + ") ]";
    }
}

运行结果:

支持java的基本类型和自带的实现了Serializable接口的类

import java.io.FileInputStream;   
import java.io.FileOutputStream;   
import java.io.ObjectInputStream;   
import java.io.ObjectOutputStream;   
import java.io.Serializable;   
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
  
public class SerialTest2 { 
    private static final String TMP_FILE = ".serialabletest2.txt";
  
    public static void main(String[] args) {   
        testWrite();
        testRead();
    }
  

    private static void testWrite() {   
        try {
            ObjectOutputStream out = new ObjectOutputStream(
                    new FileOutputStream(TMP_FILE));
            out.writeBoolean(true);  
            out.writeByte((byte)65);
            out.writeChar('a');    
            out.writeInt(20131015); 
            out.writeFloat(3.14F); 
            out.writeDouble(1.414D);

            HashMap map = new HashMap();
            map.put("one", "red");
            map.put("two", "green");
            map.put("three", "blue");
            out.writeObject(map);

            out.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
 

    private static void testRead() {
        try {
            ObjectInputStream in = new ObjectInputStream(
                    new FileInputStream(TMP_FILE));
            System.out.printf("boolean:%b\n" , in.readBoolean());
            System.out.printf("byte:%d\n" , (in.readByte()&0xff));
            System.out.printf("char:%c\n" , in.readChar());
            System.out.printf("int:%d\n" , in.readInt());
            System.out.printf("float:%f\n" , in.readFloat());
            System.out.printf("double:%f\n" , in.readDouble());

            HashMap map = (HashMap) in.readObject();
            Iterator iter = map.entrySet().iterator();
            while (iter.hasNext()) {
                Map.Entry entry = (Map.Entry)iter.next();
                System.out.printf("%-6s -- %s\n" , entry.getKey(), entry.getValue());
            }

            in.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果

这里HashMap是java.util包中定义的类,它属于java自带的实现Serializable接口的类,它的接口声明如下:

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {} 

0x03 序列化中的特例

从上面说得,我们知道序列化/反序列化,只支持保存/恢复对象状态,即仅支持保存/恢复类的成员变量,但不支持保存类的成员方法,但是,序列化是不是对类的所有的成员变量的状态都能保存呢?答案是否定的。

  1. 序列化对static和transient变量,是不会自动进行状态保存的。
    transient的作用就是,用transient声明的变量,不会被自动序列化。
  2. 对于Socket, Thread类,不支持序列化。若实现序列化的接口中,有Thread成员;在对该类进行序列化操作时,编译会出错!

static与transient
首先说一下序列化对static和transient的处理吧,我们将之前的代码中Box类修改一下

class Box implements Serializable {
    private static int width;   
    private transient int height; 
    private String name;   

    public Box(String name, int width, int height) {
        this.name = name;
        this.width = width;
        this.height = height;
    }

    public String toString() {
        return "["+name+": ("+width+", "+height+") ]";
    }
}

将成员变量的类型修改为static和transient,运行一下,结果:

前面说得,序列化不对static和transient变量进行状态保存的。因此,testWrite()中保存Box对象时,不 会保存width和height的值。但是为什么testRead()读出来的Box对象中width=80,而height=0呢?

对于height,因为Box对象中height是int类型,而int类型默认是0,因此height为0.

而对于width,它是static类型,而static类型意味着所有Box对象都公用一个heith值,而在testWrite()中,我们已经将其初始化为80,因此,我们通过序列化读出来width也是80.

那么,如果我们想要保存static或transient变量,也是可以的,只要重写两个方法writeObject()和readObject()即可。
还是Box类

class Box implements Serializable {
    private static int width;   
    private transient int height; 
    private String name;   

    public Box(String name, int width, int height) {
        this.name = name;
        this.width = width;
        this.height = height;
    }

    private void writeObject(ObjectOutputStream out) throws IOException{ 
        out.defaultWriteObject();
        out.writeInt(height); 
        out.writeInt(width); 
    }

    private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException{ 
        in.defaultReadObject();
        height = in.readInt(); 
        width = in.readInt();
    }

    public String toString() {
        return "["+name+": ("+width+", "+height+") ]";
    }
}

在writeObject()方法中,out.defaultWriteObject()是使定制的writeObject()方法可以利用自动序列化中内置的逻辑

在readObject()方法中,in.defaultReadObject()也是使定制的readObject()方法可以利用自动序列化中内置的逻辑。

Socket、Thread类

在Box类中添加

    private Thread thread = new Thread() {

        public void run() {
            System.out.println("Serializable thread");
        }
    };

运行发现,直接编译报错!

事实证明,不能对Thread进行序列化,若希望程序能便宜通过,我们对Thread变量添加static或transient修饰符即可。

0x04 完全定制序列化过程Externalizable

如果一个类要完全负责自己的序列化,则实现Externalizable接口,而不是Serializable接口。

Externalizable接口定义包括两个方法writeExternal()与readExternal()。需要注意的是:声明类实现Externalizable接口会有重大的安全风险。writeExternal()与readExternal()方法声明为public,恶意类可以用这些方法读取和写入对象数据。如果对象包含敏感信息,则要格外小心。
实例:

import java.io.FileInputStream;   
import java.io.FileOutputStream;   
import java.io.ObjectInputStream;   
import java.io.ObjectOutputStream;   
import java.io.ObjectOutput;   
import java.io.ObjectInput;   
import java.io.Serializable;   
import java.io.Externalizable;   
import java.io.IOException;   
import java.lang.ClassNotFoundException;   
  
public class ExternalizableTest1 { 
    private static final String TMP_FILE = ".externalizabletest1.txt";
  
    public static void main(String[] args) {   

        testWrite();
        testRead();
    }
  
    private static void testWrite() {   
        try {

            ObjectOutputStream out = new ObjectOutputStream(
                    new FileOutputStream(TMP_FILE));
            Box box = new Box("desk", 80, 48);
            out.writeObject(box);
            System.out.println("testWrite box: " + box);

            out.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
 
    private static void testRead() {
        try {

            ObjectInputStream in = new ObjectInputStream(
                    new FileInputStream(TMP_FILE));
            Box box = (Box) in.readObject();
            System.out.println("testRead  box: " + box);
            in.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class Box implements Externalizable {
    private int width;   
    private int height; 
    private String name;   

    public Box() {
    }

    public Box(String name, int width, int height) {
        this.name = name;
        this.width = width;
        this.height = height;
    }

    public void writeExternal(ObjectOutput out) throws IOException {
    }

    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    }

    public String toString() {
        return "["+name+": ("+width+", "+height+") ]";
    }
}

0x05 参考文献

http://www.cnblogs.com/skywang12345/p/io_06.html

上一篇下一篇

猜你喜欢

热点阅读