美文网首页
【JAVA】序列化与反序列化

【JAVA】序列化与反序列化

作者: Pino_HD | 来源:发表于2017-10-23 16:26 被阅读0次

    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

    相关文章

      网友评论

          本文标题:【JAVA】序列化与反序列化

          本文链接:https://www.haomeiwen.com/subject/maoouxtx.html