zoukankan      html  css  js  c++  java
  • Java IO流详解(七)——对象流(序列化与反序列化)

           对象流的主要用作是对Java对象的序列化和反序列化的操作。在Java IO流中提供了两个对象流:ObjectInputStream和ObjectOutputStream,这两个类都属于字节流。其中ObjectOutputStream将Java对象以字节序列的形式写出到文件,实现对象的永久存储,它继承自OutputStream。ObjectInputStream是将之前使用ObjectOutputStream序列化的字节序列恢复为Java对象,它继承自InputStream。

       序列化与反序列化

          序列化 : 把Java对象转换成字节序列的过程。

          反序列化:把序列化成字节序列的数据恢复为Java对象的过程。

        为什么需要序列化?

           ①、把对象的字节序列永久地保存到硬盘上:对于一个存在JVM中的对象来说,其内部的状态只是保存在内存中。当JVM退出之后,内存资源也就被释放,Java对象的内部状态也就丢失了。而在很多情况下,对象内部状态是需要被持久化的,将运行中的对象状态保存下来(最直接的方式就是保存到文件系统中),在需要的时候可以还原,即使是在Java虚拟机退出的情况下。

           ②、在网络上传送对象的字节序列:对象序列化机制是Java内建的一种对象持久化方式,可以很容易实现在JVM中的活动对象与字节数组(流)之间进行转换,使用得Java对象可以被存储,可以被网络传输,在网络的一端将对象序列化成字节流,经过网络传输到网络的另一端,可以从字节流重新还原为Java虚拟机中的运行状态中的对象。

    1、ObjectOutputStream类

          ObjectOutputStream代表对象输出流,即序列化,将Java对象以字节序列的形式写出到文件,实现对象的永久存储。

          它的writeObject(Object obj)方法可对指定的obj参数对象进行序列化。

          首先需要明确的一点是:一个对象要想序列化,该对象必须要实现Serializable接口,否则会抛出NotSerializableException异常。

          定义一个Person类,实现Serializable接口:

    package com.thr;
    
    import java.io.Serializable;
    
    /**
     * @author Administrator
     * @date 2020-02-28
     * @desc Person对象
     */
    public class Person implements Serializable {
        private int id;
        private String name;
        private int age;
    
        public Person() {
        }
    
        public Person(int id, String name, int age) {
            this.id = id;
            this.name = name;
            this.age = age;
        }
        //getter、setter、toString方法省略(自己测试需要加上)
    }
    

       序列化操作:

    package com.thr;
    
    import java.io.*;
    
    /**
     * @author Administrator
     * @date 2020-02-28
     * @desc 使用ObjectOutputStream序列化对象
     */
    public class ObjectOutputStreamTest {
    
        public static void main(String[] args) {
            //定义对象流
            ObjectOutputStream oos = null;
    
            try {
                //创建对象流
                oos = new ObjectOutputStream(new FileOutputStream("D:\IO\person.txt"));
                //序列化对象
                oos.writeObject(new Person(10001,"张三",20));
                oos.writeObject(new Person(10002,"李四",21));
                //刷新缓冲区
                oos.flush();
                System.out.println("序列化成功...");
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                //释放资源
                if (oos!=null){
                    try {
                        oos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

       序列化之后打开的文件我们是看不懂的,因为它是字节序列文件,只有计算机懂。所以接下来需要将它反序列化成我们能看懂的。

    2、ObjectInputStream类

         ObjectOutputStream代表对象输入流,即反序列化,将之前使用ObjectOutputStream序列化的字节序列恢复为Java对象。

         它的readObject()方法读取指定目录下的序列化对象。

         反序列化操作:

    package com.thr;
    
    import java.io.*;
    
    /**
     * @author Administrator
     * @date 2020-02-28
     * @desc 使用ObjectInputStream反序列化对象
     */
    public class ObjectInputStreamTest {
    
        public static void main(String[] args) {
            //定义对象流
            ObjectInputStream ois = null;
            try {
                //创建对象输入流对象
                ois = new ObjectInputStream(new FileInputStream("D:\IO\person.txt"));
                //反序列化对象
                Person person1 = (Person) ois.readObject();
                Person person2 = (Person) ois.readObject();
                System.out.println(person1);
                System.out.println(person2);
                System.out.println("反序列化成功...");
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } finally {
                if (ois!=null){
                    try {
                        ois.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

          在使用ObjectInputStream反序列化时需要注意一点:

          ①、在完成序列化操作后,如果对序列化对象进行了修改,比如增加某个字段,那么我们再进行反序列化就会抛出InvalidClassException异常,这种情况叫不兼容问题。

          解决的方法是:在对象中手动添加一个 serialVersionUID 字段,用来声明一个序列化版本号,之后再怎么添加属性也能进行反序列化,凡是实现Serializable接口的类都应该有一个表示序列化版本标识符的静态变量。

    public class Person implements Serializable {
        //序列化版本号
        private static final long serialVersionUID = 5687485987455L;
    
        private int id;
        private String name;
        private int age;
        //getter、setter、toString、构造方法省略(自己测试需要加上)
    }
    

     

        注意:ObjectInputStream和ObjectOutputStream不能序列化transient修饰的成员变量。

        Transient 关键字

        transient修饰符仅适用于变量,不适用于方法和类。在序列化时,如果我们不想序列化特定变量以满足安全约束,那么我们应该将该变量声明为transient。执行序列化时,JVM会忽略transient变量的原始值并将默认值保存到文件中。因此,transient意味着不要序列化。

           假如Person类中的age属性不需要序列化,在age属性上添加transient关键字。private transient int age;

    package com.thr;
    
    import java.io.*;
    
    /**
     * @author Administrator
     * @date 2020-02-29
     * @desc transient的使用
     */
    public class Test {
        public static void main(String[] args) {
            serialization(new Person(10001, "赵六", 20));
            deserialization();
        }
    
        //序列化
        public static void serialization(Person person){
            ObjectOutputStream oos = null;
            try {
                //创建输出对象流
                oos = new ObjectOutputStream(new FileOutputStream("D:\IO\object.txt"));
                //序列化对象
                oos.writeObject(person);
                oos.flush();
                System.out.println("序列化成功...");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                //释放资源
                try {
                    if (oos!=null){
                        oos.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        //反序列化
        public static void deserialization (){
            ObjectInputStream ois = null;
            try {
                //创建输入对象流
                ois = new ObjectInputStream(new FileInputStream("D:\IO\object.txt"));
                //反序列化对象
                Person person = (Person) ois.readObject();
                System.out.println(person);
                System.out.println("反序列化成功...");
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } finally {
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    
    transient的使用示例
    transient的使用示例

          运行的结果为:Person{id=10001, name='赵六', age=0},可以发现,尽管age属性没有序列化,但是它是有默认值的。

  • 相关阅读:
    67. Add Binary
    66. Plus One
    64. Minimum Path Sum
    63. Unique Paths II
    How to skip all the wizard pages and go directly to the installation process?
    Inno Setup打包之先卸载再安装
    How to change the header background color of a QTableView
    Openstack object list 一次最多有一万个 object
    Openstack 的 Log 在 /var/log/syslog 里 【Ubuntu】
    Git 分支
  • 原文地址:https://www.cnblogs.com/tanghaorong/p/12363664.html
Copyright © 2011-2022 走看看