精华内容
下载资源
问答
  • 序列化与反序列化

    千次阅读 多人点赞 2018-10-06 21:44:30
    序列化的意义 序列化面临的挑战 基于JDK 序列化方式实现 序列化的高阶认识 serialVersionUID 的作用 静态变量序列化 父类的序列化 Transient 关键字 常见的序列化技术 JAVA序列化框架 XML 序列化框架 ...

    目录

    序列化的意义

    序列化面临的挑战

    基于JDK 序列化方式实现

    序列化的高阶认识

    serialVersionUID 的作用

    静态变量序列化

    父类的序列化

    Transient 关键字

    常见的序列化技术

    JAVA序列化框架

    XML 序列化框架

    JSON 序列化框架

    Hessian 序列化框架

    Protobuf 序列化框架

    序列化技术的选型


    序列化的意义

    Java 平台允许我们在内存中创建可复用的Java 对象,但一般情况下,只有当JVM 处于运行时,这些对象才可能存在,即这些对象的生命周期不会比JVM 的生命周期更长。但在现实应用中,就可能要求在JVM停止运行之后能够保存(持久化)指定的对象,并在将来重新读取被保存的对象。Java 对象序列化就能够帮助我们实现该功能。

    简单来说,序列化是把对象的状态信息转化为可存储或传输的形式过程,也就是把对象转化为字节序列的过程称为对象的序列化。反序列化是序列化的逆向过程,把字节数组反序列化为对象,把字节序列恢复为对象的过程成为对象的反序列化。

    序列化面临的挑战

    评价一个序列化算法优劣的两个重要指标是:

    一. 序列化以后的数据大小;

    二. 序列化操作本身的速度及系统资源开销(CPU、内存)。

    Java 语言本身提供了对象序列化机制,也是Java 语言本身最重要的底层机制之一。在Java 中,只要一个类实现了java.io.Serializable 接口,那么它就可以被序列化。

    基于JDK 序列化方式实现

    JDK 提供了Java 对象的序列化方式,主要通过输出流java.io.ObjectOutputStream 和输入流java.io.ObjectInputStream来实现。其中,被序列化的对象需要实现java.io.Serializable 接口。

    import java.io.*;
    import java.util.Date;
    
    public class ObjectSaver {
        public static void main(String[] args) throws Exception {        
            //序列化对象
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("D:\\temp.txt"));
            Customer customer = new Customer("张三", 24);    
            out.writeObject("你好!");    //写入字面值常量
            out.writeObject(new Date());    //写入匿名Date对象
            out.writeObject(customer);    //写入customer对象
            out.close();
    
            
            //反序列化对象
            ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:\\temp.txt"));
            System.out.println("obj1 " + (String) in.readObject());    //读取字面值常量
            System.out.println("obj2 " + (Date) in.readObject());    //读取匿名Date对象
            Customer obj3 = (Customer) in.readObject();    //读取customer对象
            System.out.println("obj3 " + obj3);
            in.close();
        }
    }
    
    class Customer implements Serializable {
        private String name;
        private int age;
        public Customer(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public String toString() {
            return "name=" + name + ", age=" + age;
        }
    }

    序列化的高阶认识

    serialVersionUID 的作用

    Java 的序列化机制是通过判断类的serialVersionUID 来验证版本一致性的。在进行反序列化时,JVM 会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID 进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。

    当实现java.io.Serializable 接口的类没有显式地定义一个serialVersionUID 变量时候,Java 序列化机制会根据编译的Class 自动生成一个唯一的serialVersionUID,这种情况下,如果Class 文件的类名、方法名、属性名等没有发生变化,只是增加空格、换行、注释等,就算再编译多次,serialVersionUID 也不会变化。

    静态变量序列化

    序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。

    父类的序列化

    一. 当父类没有实现Serializable 接口,子类继承该父类并且实现了Serializable 接口,在反序列化该子类后,将无法获取到父类的属性值的;

    二. 当父类实现了Serializable 接口,子类将自动支持序列化功能,不需要再显式实现Serializable 接口;

    三. 当一个对象的实例变量引用了其他对象,序列化该对象时也会把引用对象进行序列化,但是前提是该引用对象必须实现了Serializable 接口。

    Transient 关键字

    Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,Transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。

    绕开Transient 机制实现自定义序列化

    /**
      * ObjectOutputStream使用了反射来寻找是否声明了该方法
      * 实现自定义的序列化操作
      */
    private void writeObject(java.io.ObjectOutputStream s)throws java.io.IOException
    
    /**
      * ObjectInputStream使用了反射来寻找是否声明了该方法
      * 实现自定义的反序列化操作
      */
    private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException
    
    /**
      * ObjectInputStream使用了反射来寻找是否声明了该方法
      * 在使用单例时,如果使用的不是枚举的实现形式,为了保证反序列化出来后的对象,不会破坏单例的情况
      * 使用该方法返回原有实例
      */
    private Object readResolve()

    以HashMap为例,为何HashMap要自己实现writeObject和readObject方法?

    由于大部分情况序列化和反序列化所在的机器是不同的,而序列化和反序列化的一个最基本的要求就是,反序列化之后的对象与序列化之前的对象是一致的。HashMap中,由于Node 的存放位置是根据Key的Hash值来计算,然后存放到数组中的,对于同一个Key 在不同的JVM实现中计算得出的Hash值可能是不同的。 Hash值不同就有可能导致一个HashMap对象的反序列化结果与序列化之前的结果不一致。

    为了避免这个问题,HashMap采用了下面的方式来解决:

    一. 将可能会造成数据不一致的元素使用transient关键字修饰,从而避免JDK中默认序列化方法对该对象的序列化操作。不序列化的包括:Node<K,V>[] table,Set<Map.Entry<K,V>> entrySet,int size,int modCount。

    二. 自己实现writeObject和readObject方法。在序列化的时候不会将保存数据的数组序列化,而是将元素个数以及每个元素的Key和Value都进行序列化。在反序列化的时候,重新计算Key和Value的位置,重新填充一个数组,从而保证序列化和反序列化结果的一致性。 

    常见的序列化技术

    JAVA序列化框架

    使用JAVA 进行序列化有他的优点,也有他的缺点。

    优点:JAVA 语言本身提供,使用比较方便和简单。

    缺点:不支持跨语言处理、 性能相对不是很好,序列化以后产生的数据相对较大。

    XML 序列化框架

    XML 序列化的好处在于可读性好,方便阅读和调试。但是序列化以后的字节码文件比较大,而且效率不高,适用于对性能不高,而且QPS 较低的企业级内部系统之间的数据交换的场景,同时XML 又具有语言无关性,所以还可以用于异构系统之间的数据交换和协议。比如我们熟知的Webservice,就是采用XML 格式对数据进行序列化的。

    JSON 序列化框架

    JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,相对于XML 来说,JSON 的字节流更小,而且可读性也非常好。现在JSON 数据格式在企业运用是最普遍的。

    JSON 序列化常用的开源工具有很多:

    1. Jackson (https://github.com/FasterXML/jackson)
    2. 阿里开源的FastJson (https://github.com/alibaba/fastjon)
    3. Google 的GSON (https://github.com/google/gson)

    这几种json 序列化工具中,Jackson 与fastjson 要比GSON 的性能要好,但是Jackson、GSON 的稳定性要比Fastjson 好。而fastjson 的优势在于提供的api 非常容易使用。

    Hessian 序列化框架

    Hessian 是一个支持跨语言传输的二进制序列化协议,相对于Java 默认的序列化机制来说,Hessian 具有更好的性能和易用性,而且支持多种不同的语言。实际上Dubbo 采用的就是Hessian 序列化来实现,只不过Dubbo 对Hessian 进行了重构,性能更高。

    Protobuf 序列化框架

    Protobuf 是Google 的一种数据交换格式,它独立于语言、独立于平台。Google 提供了多种语言来实现,比如Java、C、Go、Python,每一种实现都包含了相应语言的编译器和库文件。Protobuf 使用比较广泛,主要是空间开销小和性能比较好,非常适合用于公司内部对性能要求高的RPC 调用。 另外由于解析性能比较高,序列化以后数据量相对较少,所以也可以应用在对象的持久化场景中。但是但是要使用Protobuf 会相对来说麻烦些,因为他有自己的语法,有自己的编译器。

    序列化技术的选型

    考虑因素:

    1. 序列化空间开销,也就是序列化产生的结果大小,这个影响到传输的性能;

    2. 序列化过程中消耗的时长,序列化消耗时间过长影响到业务的响应时间;

    3. 序列化协议是否支持跨平台,跨语言。因为现在的架构更加灵活,如果存在异构系统通信需求,那么这个是必须要考虑的;

    4. 可扩展性/兼容性,在实际业务开发中,系统往往需要随着需求的快速迭代来实现快速更新,这就要求我们采用的序列化协议基于良好的可扩展性/兼容性,比如在现有的序列化数据结构中新增一个业务字段,不会影响到现有的服务;

    5. 技术的流行程度,越流行的技术意味着使用的公司多,技术解决方案也相对成熟;

    6. 学习难度和易用性。

    选型建议:

    1. 对性能要求不高的场景,可以采用基于XML 的SOAP 协议;

    2. 对性能和间接性有比较高要求的场景,那么Hessian、Protobuf、Thrift、Avro 都可以。

    3. 基于前后端分离,或者独立的对外的api 服务,选用JSON 是比较好的,对于调试、可读性都很不错;

    4. Avro 设计理念偏于动态类型语言,那么这类的场景使用Avro 是可以的。

     

    展开全文
  • 1.序列化与反序列化的概念 2.子类实现Serializable接口,父类没有实现,子类可以序列化吗? 3.类中存在引用对象,这个类对象在什么情况下可以实现序列化? 4.同一个对象多次序列化之间有属性更新,前后的序列化有...

    1.序列化与反序列化的概念

    先说说序列化和反序列化的概念

    序列化:将对象写入到IO流中
    反序列化:从IO流中恢复对象

    Serializable接口是一个标记接口,不用实现任何方法,标记当前类对象是可以序列化的,是给JVM看的。

      序列化机制允许将这些实现序列化接口的对象转化为字节序列,这些字节序列可以保证在磁盘上或者网络传输后恢复成原来的对象。序列化就是把对象存储在JVM以外的地方,序列化机制可以让对象脱离程序的运行而独立存在。

    序列化在业务代码也许用的不多,但是在框架层面用的是很多的。

    相关技术:Session的序列化或者反序列化

    先给出序列化的例子,请记住这个People类,后面会根据这个类来改造讲解。

    public class People {
    
        private Long id;
    
        public People(Long id) {
            this.id = id;
        }
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        @Override
        public String toString() {
            return "People{" +
                    "id=" + id +
                    '}';
        }
    }
    
    
    import java.io.*;
    
    // 屏蔽编译器的警告
    @SuppressWarnings("all")
    public class Main {
    
        /**
         * <h1>序列化和反序列化 People 对象</h1>
         */
        private static void testSerializablePeople() throws Exception {
    
            // 序列化的步骤
    
            // 用于存储序列化的文件,这里的java_下划线仅仅为了说明是java序列化对象,没有任何其他含义
            File file = new File("/tmp/people_10.java_");
            if (!file.exists()) {
                // 1,先得到文件的上级目录,并创建上级目录
                file.getParentFile().mkdirs();
                try {
                    // 2,再创建文件
                    file.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            People p = new People(10L);
    
            // 创建一个输出流
            ObjectOutputStream oos = new ObjectOutputStream(
                    new FileOutputStream(file)
            );
            // 输出可序列化对象
            oos.writeObject(p);
            // 关闭输出流
            oos.close();
    
            // 反序列化的步骤
    
            // 创建一个输入流
            ObjectInputStream ois = new ObjectInputStream(
                    new FileInputStream(file)
            );
            // 得到反序列化的对象,这里可以强转为People类型
            Object newPerson = ois.readObject();
            // 关闭输入流
            ois.close();
    
            System.out.println(newPerson);
        }
    
        public static void main(String[] args) throws Exception {
            testSerializablePeople();
        }
    }
    

    运行之后,看到磁盘文件因为序列化而多了一个文件

    控制台中因反序列化输出的对象信息打印如下:


    2.子类实现Serializable接口,父类没有实现,子类可以序列化吗?

    去掉父类Peopleimplements Serializable,让父类不实现序列化接口,子类Worker实现序列化接口

    public class Worker extends People implements Serializable {
    
        private String name;
        private Integer age;
    
        public Worker(Long id, String name, Integer age) {
            super(id);
            this.name = name;
            this.age = age;
        }
    
    }
    
    
    	public static void main(String[] args) throws Exception {
            testSerizableWorker();
        }
    
        /**
         * <h2>子类实现序列化, 父类不实现序列化</h2>
         * */
        private static void testSerizableWorker() throws Exception {
    
            File file = new File("/tmp/worker_10.java_");
            if (!file.exists()) {
                // 1,先得到文件的上级目录,并创建上级目录
                file.getParentFile().mkdirs();
                try {
                    // 2,再创建文件
                    file.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            Worker p = new Worker(10L, "lcy", 18);
    
            // 创建一个输出流
            ObjectOutputStream oos = new ObjectOutputStream(
                    new FileOutputStream(file)
            );
            // 输出可序列化对象
            oos.writeObject(p);
            // 关闭输出流
            oos.close();
    
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
            Object newWorker = ois.readObject(); // 父类没有序列化的时候,需要调用父类的无参数构造方法
            ois.close();
            System.out.println(newWorker);
        }
    

    再次测试运行

      结果显示没有有效地构造器,原来是因为父类没有序列化的时候,Object newWorker = ois.readObject()需要直接调用父类的无参数构造方法,不经过子类的无参构造方法。

    我们在父类People中加上空的构造方法之后再次执行

      结果却发现打印的不是Worker,而是父类People,因为子类没有实现toString而调用父类的toString,所以打印了People对象,至于父类成员变量id为什么是null,原因如下:

      一个子类实现了 Serializable 接口,它的父类都没有实现 Serializable接口,序列化该子类对象。要想反序列化后输出父类定义的某变量的数值,就需要让父类也实现Serializable接口或者父类有默认的无参的构造函数。

      在父类没有实现Serializable 接口时,虚拟机是不会序列化父对象的,而一个 Java对象的构造必须先有父对象,才有子对象,反序列化也不例外。所以反序列化时,为了构造父对象,只能调用父类的无参构造函数作为默认的父对象。因此当我们取父对象的变量值时,它的值是调用父类无参构造函数后的值,如果在父类无参构造函数中没有对变量赋值,那么父类成员变量值都是默认值,如这里的Long型就是null

      根据以上特性,我们可以将不需要被序列化的字段抽取出来放到父类中,子类实现 Serializable接口,父类不实现Serializable接口但提供一个空构造方法,则父类的字段数据将不被序列化。

    最后加上子类WorkertoString方法,打印结果如下:

    总结:
    子类实现Serializable接口,父类没有实现,子类可以序列化!!
    这种情况父类一定要提供空构造方法,不要忘了子类的toString方法!


    3.类中存在引用对象,这个类对象在什么情况下可以实现序列化?

      来一个组合对象,里面引用People对象,此时People对象没有实现Serializable接口,能否序列化呢?代码给出来,大家可以自行复制测试一下。

    public class Combo implements Serializable {
    
        private int id;
        private People people;
    
        public Combo(int id, People people) {
            this.id = id;
            this.people = people;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public People getPeople() {
            return people;
        }
    
        public void setPeople(People people) {
            this.people = people;
        }
        
        @Override
        public String toString() {
            return "Combo{" +
                    "id=" + id +
                    ", people=" + people +
                    '}';
        }
    }
    
    public class People {
    
        private Long id;
    
        public People() {
        }
    
        public People(Long id) {
            this.id = id;
        }
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        @Override
        public String toString() {
            return "People{" +
                    "id=" + id +
                    '}';
        }
    }
    
        private static void testSerializableCombo() throws Exception {
    
            File file = new File("/tmp/combo_10.java_");
            if (!file.exists()) {
                // 1,先得到文件的上级目录,并创建上级目录
                file.getParentFile().mkdirs();
                try {
                    // 2,再创建文件
                    file.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            Combo p = new Combo(1, new People(10L));
    
            // 创建一个输出流
            ObjectOutputStream oos = new ObjectOutputStream(
                    new FileOutputStream(file)
            );
            // 输出可序列化对象
            oos.writeObject(p);
            // 关闭输出流
            oos.close();
    
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
            Object newCombo = ois.readObject();
            ois.close();
            System.out.println(newCombo);
        }
    
        public static void main(String[] args) throws Exception {
            testSerializableCombo();
        }
    

    运行结果如下

    直接爆出异常,说明People类没有序列化。
    People加上implements Serializable实现序列化接口后,再次执行如下

    总结:
      一个类里面所有的属性必须是可序列化的,这个类才能顺利的序列化。比如,类中存在引用对象,那么这个引用对象必须是可序列化的,这个类才能序列化。


    4.同一个对象多次序列化之间有属性更新,前后的序列化有什么区别?

      下面例子中People是可序列化的,每次序列化之前都会把Peopleid值修改了,用来观察看看,多次序列化期间,如果对象属性更新,是否会影响序列化,反序列化有什么区别。

        /**
         * <h2>同一个对象多次序列化的问题, 坑</h2>
         * */
        private static void sameObjectRepeatedSerialization() throws Exception {
    
            File file = new File("/tmp/peopele_more.java_");
            if (!file.exists()) {
                // 1,先得到文件的上级目录,并创建上级目录
                file.getParentFile().mkdirs();
                try {
                    // 2,再创建文件
                    file.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            People p = new People(10L);
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
            // 未序列化,先修改属性
            p.setId(11L);
            oos.writeObject(p);
            // 序列化一次后,再次修改属性
            p.setId(15L);
            oos.writeObject(p);
            // 序列化两次后,再次修改属性
            p.setId(20L);
            oos.writeObject(p);
            oos.close();
    
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
            Object people1 = ois.readObject();
            Object people2 = ois.readObject();
            Object people3 = ois.readObject();
            ois.close();
    
            System.out.println(((People) people1).getId());
            System.out.println(((People) people2).getId());
            System.out.println(((People) people3).getId());
        }
    
    
        public static void main(String[] args) throws Exception {
            sameObjectRepeatedSerialization();
        }
    

    运行结果如下

      结果发现反序列化读出的值都是一样的。说明当对象第一次序列化成功后,后续这个对象属性即使有修改,也不会对后面的序列化造成成影响。

      这其实是序列化算法的原因,所有要序列化的对象都有一个序列化的编码号,当试图序列化一个对象,会检查这个对象是否已经序列化过,若从未序列化过,才会序列化为字节序列去输出。若已经序列化过,则会输出一个编码符号,不会重复序列化一个对象。如下

    序列化一次后,后续继续序列化并未重复转换为字节序列,而是输出字符q~

    总结:
      当第一次序列化之后,不管如何修改这个对象的属性,都不会对后续的序列化产生影响,反序列化的结果都和第一次相同。



    欢迎一键三连~

    有问题请留言,大家一起探讨学习

    ----------------------Talk is cheap, show me the code-----------------------
    展开全文
  • Java序列化与反序列化

    万次阅读 多人点赞 2012-09-18 16:48:57
    Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?本文围绕这些问题进行了探讨。  1.Java序列化与反序列化  Java序列化是指把Java对象转换为字节序列的过程;而Java反...
    Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?本文围绕这些问题进行了探讨。 
    

     1.Java序列化与反序列化

     Java序列化是指把Java对象转换为字节序列的过程;而Java反序列化是指把字节序列恢复为Java对象的过程。

     2.为什么需要序列化与反序列化

     我们知道,当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本、图片、音频、视频等, 而这些数据都会以二进制序列的形式在网络上传送。那么当两个Java进程进行通信时,能否实现进程间的对象传送呢?答案是可以的。如何做到呢?这就需要Java序列化与反序列化了。换句话说,一方面,发送方需要把这个Java对象转换为字节序列,然后在网络上传送;另一方面,接收方需要从字节序列中恢复出Java对象。

     当我们明晰了为什么需要Java序列化和反序列化后,我们很自然地会想Java序列化的好处。其好处一是实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上(通常存放在文件里),二是,利用序列化实现远程通信,即在网络上传送对象的字节序列。

    3.如何实现Java序列化与反序列化

    1)JDK类库中序列化API

     java.io.ObjectOutputStream:表示对象输出流

    它的writeObject(Object obj)方法可以对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。

    java.io.ObjectInputStream:表示对象输入流

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

    2)实现序列化的要求

    只有实现了Serializable或Externalizable接口的类的对象才能被序列化,否则抛出异常。

    3)实现Java对象序列化与反序列化的方法

    假定一个Student类,它的对象需要序列化,可以有如下三种方法:

    方法一:若Student类仅仅实现了Serializable接口,则可以按照以下方式进行序列化和反序列化

    ObjectOutputStream采用默认的序列化方式,对Student对象的非transient的实例变量进行序列化。

    ObjcetInputStream采用默认的反序列化方式,对对Student对象的非transient的实例变量进行反序列化。

    方法二:若Student类仅仅实现了Serializable接口,并且还定义了readObject(ObjectInputStream in)和writeObject(ObjectOutputSteam out),则采用以下方式进行序列化与反序列化。

    ObjectOutputStream调用Student对象的writeObject(ObjectOutputStream out)的方法进行序列化。

    ObjectInputStream会调用Student对象的readObject(ObjectInputStream in)的方法进行反序列化。

    方法三:若Student类实现了Externalnalizable接口,且Student类必须实现readExternal(ObjectInput in)和writeExternal(ObjectOutput out)方法,则按照以下方式进行序列化与反序列化。

    ObjectOutputStream调用Student对象的writeExternal(ObjectOutput out))的方法进行序列化。

    ObjectInputStream会调用Student对象的readExternal(ObjectInput in)的方法进行反序列化。

    4)JDK类库中序列化的步骤

    步骤一:创建一个对象输出流,它可以包装一个其它类型的目标输出流,如文件输出流:

    ObjectOutputStream out = new ObjectOutputStream(new fileOutputStream(“D:\\objectfile.obj”));

    步骤二:通过对象输出流的writeObject()方法写对象:

    out.writeObject(“Hello”);

    out.writeObject(new Date());

    5)JDK类库中反序列化的步骤

    步骤一:创建一个对象输入流,它可以包装一个其它类型输入流,如文件输入流:

    ObjectInputStream in = new ObjectInputStream(new fileInputStream(“D:\\objectfile.obj”));

    步骤二:通过对象输出流的readObject()方法读取对象:

    String obj1 = (String)in.readObject();

    Date obj2 = (Date)in.readObject();

    说明:为了正确读取数据,完成反序列化,必须保证向对象输出流写对象的顺序与从对象输入流中读对象的顺序一致。

    为了更好地理解Java序列化与反序列化,选择方法一编码实现。

    Student类定义如下:

    package com.jieke.io;
    import java.io.Serializable;
    
    /**
     *Title:学生类
     *Description:实现序列化接口的学生类
     *Copyright: copyright(c) 2012
     *Filename: Student.java
     *@author Wang Luqing
     *@version 1.0
     */
    public class Student implements Serializable
    {
     private String name;
     private char sex;
     private int year;
     private double gpa;
    
     public Student()
     {
    
     }
     public Student(String name,char sex,int year,double gpa)
     {
      this.name = name;
      this.sex = sex;
      this.year = year;
      this.gpa = gpa;
     }
    
     public void setName(String name)
     {
      this.name = name;
     }
    
     public void setSex(char sex)
     {
      this.sex = sex;
     }
    
     public void setYear(int year)
     {
      this.year = year;
     }
    
     public void setGpa(double gpa)
     {
      this.gpa = gpa;
     }
    
     public String getName()
     {
      return this.name;
     }
     
     public char getSex()
     {
      return this.sex;
     }
    
     public int getYear()
     {
      return this.year;
     }
    
     public double getGpa()
     {
      return this.gpa;
     }
    }

    把Student类的对象序列化到文件O:\\Java\\com\\jieke\\io\\student.txt,并从该文件中反序列化,向console显示结果。代码如下:

    import java.io.*;
    
    /**
     *Title:应用学生类
     *Description:实现学生类实例的序列化与反序列化
     *Copyright: copyright(c) 2012
     *Filename: UseStudent.java
     *@author Wang Luqing
     *@version 1.0
     */
    
    public class UseStudent
    {
     public static void main(String[] args)
     {
      Student st = new Student("Tom",'M',20,3.6);
      File file = new File("O:\\Java\\com\\jieke\\io\\student.txt");
      try
      {
       file.createNewFile();
      }
      catch(IOException e)
      {
       e.printStackTrace();
      }
      try
      {
       //Student对象序列化过程
       FileOutputStream fos = new FileOutputStream(file);
       ObjectOutputStream oos = new ObjectOutputStream(fos);
       oos.writeObject(st);
       oos.flush();
       oos.close();
       fos.close();
    
       //Student对象反序列化过程
       FileInputStream fis = new FileInputStream(file);
       ObjectInputStream ois = new ObjectInputStream(fis);
       Student st1 = (Student) ois.readObject();
       System.out.println("name = " + st1.getName());
       System.out.println("sex = " + st1.getSex());
       System.out.println("year = " + st1.getYear());
       System.out.println("gpa = " + st1.getGpa());
       ois.close();
       fis.close();
      }
      catch(ClassNotFoundException e)
      {
       e.printStackTrace();
      }
      catch (IOException e)
      {
       e.printStackTrace();
      }				
     }
    }

    结果如下所示:

    name = Tom

    sex = M

    year = 20

    gpa = 3.6

    总结:

    1)Java序列化就是把对象转换成字节序列,而Java反序列化就是把字节序列还原成Java对象。

    2)采用Java序列化与反序列化技术,一是可以实现数据的持久化,在MVC模式中很是有用;二是可以对象数据的远程通信。

    展开全文
  • Java 序列化与反序列化

    千次阅读 2020-09-05 13:04:20
    介绍 Java 的序列化与反序列化。

    Java 序列化与反序列化


    1 序列化与反序列化的概念

    • Java 序列化是指:将对象转化成一个字节序列(二进制数据)的过程。

    • 将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化。

    • Java 反序列化是指:将一个对象的字节序列恢复成 Java 对象的过程。

    • 一个平台中序列化的对象,可以在另一个平台中进行反序列化,因为这个过程是在 JVM 中独立完成的,可以依赖于 Java 的可移植性。


    2 核心类与关键字总览

    • ObjectOutputStream:IO 类,包含序列化对象的方法,writeObject()

    • ObjectInputStream:IO 类,包含反序列化对象的方法,readObject()

    • 上面两个 IO 流类是高层次的数据库,需要借助文件流进行序列化与反序列化操作。

    • Serializable ,接口,是一个标志性接口,标识可以在 JVM 中进行序列化,JVM 会为该类自动生成一个序列化版本号。参与序列化与反序列化的类必须实现 Serializable 接口。

    • serialVersionUID,类属性,序列化版本号,用于给 JVM 区别同名类,没有提供版本号,JVM会默认提供序列化版本号。

    • transient,关键字,当序列化时,不希望某些属性参与,则可以使用这个关键字标注该属性。


    3 序列化与反序列化的过程

    • 内存中的数据信息被拆分成一小块一小块的部分,为每个小块设置编号,然后存放到硬盘文件中,也就是将 Java 对象对象的状态保存下来存储到文件中的过程就叫做序列化。

    • 将硬盘中保存了 Java 对象状态的字节序列按照编号组装成对象恢复到内存中,这个过程称为反序列化。


    3 应用示例

    参与序列化和反序列化的 Java 类

    public class Student implements Serializable {
        private String name;
        private int age;
        //  以下省略有参构造、无参构造、set、get、toString
    }
    
    • 参与序列化和反序列化的类必须实现 Serializable 接口。

    序列化操作

    public static void main(String[] args) throws Exception {
        //  创建 Java 对象
        Student student = new Student("张三",22);
        //  对象输出流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student"));
        // 使用 writeObject 序列化对象
        oos.writeObject(student);
        // 刷新
        oos.flush();
        //  关闭流
        oos.close();
    }
    
    • 序列化后的二进制文件会被保存到文件输出流指定的路径。

    反序列化操作

    public static void main(String[] args) throws Exception {
        //  对象输入流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("student"));
        //  使用 readObject() 反序列化  
        Object obj = ois.readObject();
        //  使用对象
        System.out.println(obj);
        //  关闭流
        ois.close();
    }
    
    • 反序列化需要借助文件输入流读取指定路径的二进制文件。

    4 序列化版本号的作用 serialVersionUID

    • JVM 首先会通过类名来区分 Java 类,类名不同,则不是同一个类。当类名相同时,JVM 就会通过序列化版本号来区分 Java 类,如果序列化版本号相同就为同一个类,序列化版本号不同就为不同的类。

    • 在序列化一个对象时,如果没有指定序列化版本号,后期对该类的源码进行修改并重新编译后,会导致修改前后的序列化版本号不一致,因为 JVM 会提供一个新的序列化版本号给该类对象。

    • 此时再用以往的反序列化代码去反序列化该类的对象,就会抛出异常 java.io.InvalidClassException ,所以序列化一个类时最好指定一个序列化版本号,或者永远不修改此类。

    public class Student implements Serializable {
        private static final Long serialVersionUID = 1L;
    }
    
    • 由 JVM 提供序列化版本号的好处是,同名却不同功能的类,会有两个不同的序列化版本号,JVM 可以通过序列化版本号加以区分,缺点是一旦修改源码,会重新提供序列化版本号,导致修改前后的序列化版本号不一致,进行反序列化时会出现运行出现异常。

    • 由 开发人员 手动提供序列化版本号的好处是,当修改了被序列化类的源码后,以往写的反序列化代码依然可以使用,如 JDK 中的 String 类。以便后期进行增强和维护不会影响使用。

    在这里插入图片描述


    5 transient 关键字

    • 这个关键字表示游离的,不参与序列化的。

    • 在序列化一个对象时,如果不希望某个属性参加序列化,可以使用 transient 修饰该属性。

    • 被该关键字修饰的属性不会参与到序列化中。

    public class Student implements Serializable {
    
        private static final Long serialVersionUID = 1L;
    
        private String name;
        private transient int age;
    }
    
    • 如上类,在序列化时就不会保存 age 属性,在反序列化时就不能会付出该属性,默认恢复成 null 或 0 ,由属性类型决定。

    6 序列化的好处及应用场景

    • 序列化会将内存中对象的状态转换成二进制文件保存到磁盘当中,当再次使用时会从磁盘中读取该二进制文件,将 Java 对象的状态恢复到内存中。

    • 当你想把内存中的对象保存到磁盘文件或数据库中时可以使用序列化。

    • 当你想在网络传输中传送 Java 对象时,可以使用序列化。

    • 当你想通过 RMI 传输对象时,可以使用序列化。


    7 序列化注意事项

    • 序列化只会保存对象的属性状态,不会保存对象中的方法。

    • 父类实现了 Serializable 接口,则其子类也自动实例化了该接口,也就是说子类不用显式实现 Serializable 接口也能参与序列化和反序列化。

    • 一个对象 A 的实例变量引用了其他对象 B,在 A 对象实例化的过程中 ,也会序列化 B ,前提是 A、B 两个类都实现了 Serializable 接口。

    • 当一个类实现 Serializable 接口时,最好手动指定一个序列化版本号(serialVersionUID),避免修改源代码后导致反序列化出现异常。

    • 当一个类对象会被多次重复使用,且一般不会对其属性做修改,就可以对其进行序列化。例如数据库操作中的实体类。


    参考博文:

    展开全文
  • Java实现 LeetCode 297 二叉树的序列化与反序列化

    万次阅读 多人点赞 2020-03-05 17:03:06
    297. 二叉树的序列化与反序列化 序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原...
  • C++ JSON 序列化与反序列化

    热门讨论 2013-06-22 14:23:31
    C++ JSON 序列化与反序列化 相关的博客文章见:http://blog.csdn.net/TragicGuy
  • FastJson在scala中序列化与反序列化

    千次阅读 2019-08-29 10:19:46
    FastJson在scala中序列化与反序列化 Alibaba的一款开源JSON组件FastJson,非常好用,在序列化和反序列化方面性能突出,而且API接口简单易用,算是处理JSON的一大利器 首先,添加依赖,注意这里的版本号,务必使用...
  • 新手小白学JAVA 初识序列化与反序列化

    万次阅读 多人点赞 2021-03-17 13:41:48
    序列化/反序列化 1.1 概述 序列化(Serialization)是将对象的状态信息转换为可以存储或传输形式的过程. 在序列化期间,对象将其当前状态写入到临时或持久性存储区.以后可以通过从存储区中读取或者帆序列化对象的状态,...
  • Gson自定义序列化与反序列化

    千次阅读 2018-07-28 12:49:42
    在使用Gson的时候我们经常需要根据自己的需求定义序列化反序列化的规则,本文主要讲解如何优雅地进行Gson的序列化反序列化序列化 方法 实现JsonSerializer接口 注册自定义的序列化实现 demo ...
  • 单个类序列化与反序列化: 等同于简单对象的序列化 多态类型是可以进行 序列化,但是 反序列化会出错 解决方式 : 见泛型序列化 / 多态序列化2、基本类 GsonFather 是 父类 ,GsonSon 和 GsonDaughter 是子类...
  • 在前两篇文章中,我们已经完成对普通对象以及复杂对象嵌套的序列化与反序列化,见如下地址: C++对象的JSON序列化与反序列化探索 C++对象的JSON序列化与反序列化探索续-复杂对象的序列化与反序列化 经过一番...
  • MultiValueMap的序列化与反序列化

    千次阅读 2017-09-29 20:17:45
    目前我用的fastjson版本(1.2.30)不支持序列化与反序列化,报了个不支持的类型。 项目里面只有GSON和fastjson,没有jackson,所以就google了一下,最终使用GSON找到了解决办法。序列化:new Gson().toJson...
  • C++序列化与反序列化的简单探索

    万次阅读 2016-11-11 15:47:21
    C++序列化与反序列化 初步的探索~
  • JSON序列化与反序列化

    千次阅读 2013-10-24 18:00:49
    序列化与反序列化 前台(jQuery): var objP = {};  objP.id = 1;  objP.name = 2;  objP.sex = 32; JSON.stringify(objP)//序列化 JSON.parse(e); //JSON.parse(e)反序列化   后台(.net自带):
  • 1.JAVA Object 序列化与反序列化 所谓的JAVA序列化与反序列化,序列化就是将JAVA 对象以一种的形式保持,比如存放到硬盘,或是用于传输。反序列化是序列化的一个逆过程 JAVA规定被序列化的对象必须实现java.io....
  • 秒懂Java序列化与反序列化

    千次阅读 多人点赞 2018-06-09 14:40:43
    什么是反序列化?为什么需要序列化?如何序列化?应该注意什么?本文将从这几方面来论述。 定义 什么是序列化?什么是反序列化序列化: 把Java对象转换为字节序列的过程。 反序列化:把字节序列恢复为Java...
  • 序列化与反序列化 - Java基础

    千次阅读 2020-08-22 05:29:13
    知识的广度来自知识的深度,学习如果不成体系那是多可怕的一件事儿...序列化与反序列化 - Java基础一、序列化简介二、序列化的使用三、自定义序列化功能3.1 transient关键字的使用3.2 writeObject方法的使用3.3 readO.
  • 1.序列化与反序列化 1、序列化和反序列化简介: 序列化就是指把对象转换为字节码; 对象传递和保存时,保证对象的完整性和可传递性。把对象转换为有字节码,以便在网络上传输或保存在本地文件中; 反序列化就是...
  • java序列化与反序列化

    万次阅读 2017-08-19 08:58:27
    Java序列化可以实现将我们的对象转化为字节序列,反序列化则是将字节序列转化为我们的Java对象 为什么要有Java序列化和反序列化 我们知道,当两个进程进行远程通信时,可以相互发送...这就需要Java序列化与反序列化了
  • Python中pickle序列化与反序列化

    千次阅读 2020-03-10 09:46:58
    原文地址 序列化/反序列化 ...pickle模块实现了用于对Python对象结构进行 序列化反序列化 的二进制协议,json模块不同的是pickle模块序列化反序列化的过程分别叫做 pickling 和 unpickling: pi...
  • 在深拷贝与浅拷贝中,提到可以采用「序列化与反序列化」的方式来实现深拷贝,今天主要来填一下序列化的坑。 其中,序列化是一种对象持久化的手段,普遍应用于网络传输和远程方法调用(RMI)等场景中,建议关注。 ...
  • 【Python】|JSON序列化与反序列化

    千次阅读 2016-08-18 15:10:41
    Python: JSON的序列化与反序列化
  • 背景前面一篇总结了Serializable的序列化与反序列化,现在接着总结XML。主要内容:XML基本的序列化与反序列化方法、一些注意事项、以及自定义了一个XML注解框架(简洁代码,解放双手)。XML的序列化与反序列化先与...
  • Hadoop序列化与反序列化详解

    千次阅读 多人点赞 2019-11-11 21:03:38
    反序列化就是将收到字节序列(或其他数据传输协议) 或者是磁盘的持久化数据,转换成内存中的对象 2.为什么要序列化? 一般来说,"活的"对象只能在内存中生存,关机断电就没有了,而且"活的"对象只能由本地的进程使用...
  • java序列化与反序列化工具

    千次阅读 2013-06-28 13:34:15
    java序列化与反序列化工具 package com.util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io....
  • dubbo源码分析25 -- 序列化与反序列化

    千次阅读 2018-07-02 19:52:30
    序列化:把对象转换为字节序列的过程称为对象的序列化。 反序列化:把字节序列恢复为对象的过程称为对象的反序列化。...这就需要使用到序列化与反序列化。在 dubbo 中定义了 ObjectInput、ObjectOutput 与...
  • 序列化与反序列化文件内容的读写

    千次阅读 2018-08-10 09:53:22
    复习序列化与反序列化的概念,要求自定义Person类,其中三个属性name,age,school.  age属性不作为序列化保存而其他两个属性使用序列化保存在本地文件TestSer.txt中。  使用序列化和反序列化的方式将自定义类序列化...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,180,166
精华内容 472,066
关键字:

序列化与反序列化