Java 序列化与反序列化

1、什么是序列化?为什么要序列化?

    Java 序列化就是指将对象转换为字节序列的过程,而反序列化则是只将字节序列转换成目标对象的过程。

    我们都知道,在进行浏览器访问的时候,我们看到的文本、图片、音频、视频等都是通过二进制序列进行传输的,那么如果我们需要将Java对象进行传输的时候,是不是也应该先将对象进行序列化?答案是肯定的,我们需要先将Java对象进行序列化,然后通过网络,IO进行传输,当到达目的地之后,再进行反序列化获取到我们想要的对象,最后完成通信。

2、如何实现序列化

  2.1、使用到JDK中关键类 ObjectOutputStream 和ObjectInputStream

    ObjectOutputStream 类中:通过使用writeObject(Object object) 方法,将对象以二进制格式进行写入。

    ObjectInputStream 类中:通过使用readObject()方法,从输入流中读取二进制流,转换成对象。

  2.2、目标对象需要先实现 Seriable接口

  

我们创建一个Student类:

public class Student implements Serializable {
    private static final long serialVersionUID = 3404072173323892464L;
    private String name;
    private transient String id;
    private String age;

    @Override
    public String toString() {
        return "Student{" +
                "name=‘" + name + ‘\‘‘ +
                ", id=‘" + id + ‘\‘‘ +
                ", age=‘" + age + ‘\‘‘ +
                ‘}‘;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public Student(String name, String id) {
        System.out.println("args Constructor");
        this.name = name;
        this.id = id;
    }

    public Student() {
        System.out.println("none-arg Constructor");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

}

        

      代码中Student类实现了Serializable 接口,并且生成了一个版本号:

private static final long serialVersionUID = 3404072173323892464L;

      首先:

      1、Serializable 接口的作用只是用来标识我们这个类是需要进行序列化,并且Serializable 接口中并没有提供任何方法。

      2、serialVersionUid 序列化版本号的作用是用来区分我们所编写的类的版本,用于判断反序列化时类的版本是否一直,如果不一致会出现版本不一致异常。

      3、transient 关键字,主要用来忽略我们不希望进行序列化的变量

    2.3、将对象进行序列或和反序列化

      2.3.1 第一种写入方式:

public static  void main(String[] args){
        File file = new File("D:/test.txt");
        Student student = new Student("孙悟空","12");
        try {
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(file));
            outputStream.writeObject(student);
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
            Student s = (Student) objectInputStream.readObject();
            System.out.println(s.toString());
            System.out.println(s.equals(student));
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

      创建对象Student ,然后通过ObjectOutputStream类中的writeObject()方法,将对象输出到文件中。

      然后通过ObjectinputStream 类中的readObject()方法反序列化,获取对象。

       2.3.2 第二种写入方式:

在Student 类中实现writeObject()和readObject()方法:

  private void writeObject(ObjectOutputStream objectOutputStream) throws IOException {
        objectOutputStream.defaultWriteObject();
        objectOutputStream.writeUTF(id);

    }

    private void readObject(ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException {
        objectInputStream.defaultReadObject();
        id = objectInputStream.readUTF();
    }

      通过这中方式进行序列话,我们可以自定义想要进行序列化的变量,将输入流和输出流传入对线实例中,然后进行序列化以及反序列化。

  

      2.3.3 第三种写入方式:

Student 实现 Externalnalizable接口 而不实现Serializable 接口

 Externaliable 接口是 Serializable 的子类,有着和Serializable接口同样的功能:

public class Student implements Externalizable {
    private static final long serialVersionUID = 3404072173323892464L;
    private String name;
    private transient String id;
    private String age;

    @Override
    public String toString() {
        return "Student{" +
                "name=‘" + name + ‘\‘‘ +
                ", id=‘" + id + ‘\‘‘ +
                ", age=‘" + age + ‘\‘‘ +
                ‘}‘;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public Student(String name, String id) {
        System.out.println("args Constructor");
        this.name = name;
        this.id = id;
    }

    public Student() {
        System.out.println("none-arg Constructor");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(name);
        out.writeObject(id);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        name = (String) in.readObject();
        id = (String) in .readObject();
    }

}

   通过和前面的第二种写入方法对比,我们可以发现他们的实现原理都是十分的类似,不过实现Externalnalizable接口 并不支持第一种序列化方法,它只能够通过实现接口中的writeExternal()和readExternal()方法实现对象的序列化。

3、面试中关于序列化的问题:

    1、什么是序列化,如何实现序列化

        java中对象的序列化就是将对象转换成二进制序列,反序列化则是将二进制序列转换成对象

        Java 实现序列化有多种方式

          1、首先需要使用到工具类ObjectInputStream 和ObjectOutputStream 两个IO类

          2、实现Serializable 接口:

              有两种具体序列化方法:

                  2.1 直接通过ObjectOutputStream 和 ObjectInputStream 类中的 writeObject()和readObject()方法

                  2.2 通过在序列化对象中实现writeObject()和readObject()方法,传入ObjectOutputStream和ObjectInputStream对象,完成序列化

          3、实现Externalizable 接口:

                  只能够通过实现接口中的writeExternal()和readExternal()方法实现对象的序列化

     2、transient 关键字?如何将transient修饰符修饰的变量序列化?        transient 的作用是用来屏蔽我们不希望进行序列化的变量,是对象在进行序列化和反序列话的过程中忽略该变量。        我们可以通过上述序列化方法中的 实现writeObject 和readObject 方法,在方法中调用输出流或输入流的writeUTF()和readUTF()方法。        或者通过实现Externalizable 接口,实现writeExternal()和readExternal()方法,然后再自定义序列话对象。

     
      3、如何保证序列化和反序列化后的对象一致?(如有异议望指正)        对于这个问题我在查阅了一些资料之后,发现并不能保证序列化和反序列化之后的对象是一致的,因为我们在反序列化的过程中,是先创建一个对象,        然后再通过对对象进行赋值来完成对象的反序列化,这样问题就来了,在创建了一个新的对象之后,对象引用和原本的对象并不是指向同一个目标。        因此我们只能保证他们的数据和版本一致,并不能保证对象一致。
 

  

    

时间: 2024-10-08 15:36:01

Java 序列化与反序列化的相关文章

java序列化和反序列化

一.序列化和反序列化的概念 把对象转换为字节序列的过程称为对象的序列化. 把字节序列恢复为对象的过程称为对象的反序列化. 对象的序列化主要有两种用途: 1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中: 2) 在网络上传送对象的字节序列. 在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存.比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些s

java序列化与反序列化以及浅谈一下hadoop的序列化

1.什么是序列化和反序列化 神马是序列化呢,序列化就是把内存中的对象的状态信息,转换成字节序列以便于存储(持久化)和网络传输.(网络传输和硬盘持久化,你没有一定的手段来进行辨别这些字节序列是什么东西,有什么信息,这些字节序列就是垃圾). 反序列化就是将收到字节序列或者是硬盘的持久化数据,转换成内存中的对象. 2.JDK的序列化 JDK的序列化只有实现了serializable接口就能实现序列化与反序列化,但是记得一定要加上序列化版本ID serialVersionUID 这个是识别序列化的之前那

Java序列化与反序列化(实践)

Java序列化与反序列化(实践) 基本概念:序列化是将对象状态转换为可保持或传输的格式的过程.与序列化相对的是反序列化,它将流转换为对象.这两个过程结合起来,可以轻松地存储和传输数据. 昨天在一本书上看到了,好好实践了一下,序列化为一般文件,也序列化为XML文件(使用XStream). 用于序列化的实体类Person.java    代码如下(记得需要实现Serializable接口):import java.io.Serializable; @SuppressWarnings("serial&

JAVA序列化和反序列化XML

package com.lss.utils; import java.beans.XMLDecoder; import java.beans.XMLEncoder; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; /** * JAVA序列化和反序列化X

【java基础知识】【java序列化与反序列化及序列化ID】

今天上午学习了java序列化与反序列化及serialVersionUID的用处. 一.序列化与反序列化的作用: 对象的序列化主要有两种用途:1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中:2) 在网络上传送对象的字节序列. 因为不对对象序列化的化容易出现很多问题,所以引入了序列化解决了这些问题. 对象序列化包括如下步骤:1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流:2) 通过对象输出流的writeObject()方法写对象.对象反序列化的步骤如下:1

JAVA 序列化和反序列化XML

import java.beans.XMLDecoder; import java.beans.XMLEncoder; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; /** * JAVA序列化和反序列化->XML * <p> *公共无参构造函

(记录)Java序列化与反序列化

在学习Redis中的Jedis这一部分的时候,要使用到Protostuff(Protobuf的Java客户端)这一序列化工具.一开始看到序列化这些字眼的时候,感觉到一头雾水.于是,参考了网上的很多资料,理解了Java序列化与反序列化(参考https://blog.csdn.net/wangloveall/article/details/7992448/),Protobuf的作用以及在Java中的使用方法. 1.Java序列化与反序列化是什么: Java序列化是指把Java对象转换为字节序列的过程

Java 序列化和反序列化(二)Serializable 源码分析 - 1

目录 Java 序列化和反序列化(二)Serializable 源码分析 - 1 1. Java 序列化接口 Java 序列化和反序列化(二)Serializable 源码分析 - 1 在上一篇文章中讲解了一下 Serializable 的大致用法,本节重点关注 Java 序列化的实现,围绕 ObjectOutputStream#writeObject 方法展开. 1. Java 序列化接口 Java 为了方便开发人员将 Java 对象进行序列化及反序列化提供了一套方便的 API 来支持.其中包

Java 序列化和反序列化(三)Serializable 源码分析 - 2

目录 Java 序列化和反序列化(三)Serializable 源码分析 - 2 1. ObjectStreamField 1.1 数据结构 1.2 构造函数 2. ObjectStreamClass Java 序列化和反序列化(三)Serializable 源码分析 - 2 在上一篇文章中围绕 ObjectOutputStream#writeObject 讲解了一下序列化的整个流程,这中间很多地方涉及到了 ObjectStreamClass 和 ObjectStreamField 这两个类.

java序列化与反序列化

public interface Serializable类通过实现 java.io.Serializable 接口以启用其序列化功能.未实现此接口的类将无法使其任何状态序列化或反序列化.可序列化类的所有子类型本身都是可序列化的.序列化接口没有方法或字段,仅用于标识可序列化的语义. 序列化就是将一个对象的状态(各个属性量)保存起来,然后在适当的时候再获得.         序列化分为两大部分:序列化和反序列化.序列化是这个过程的第一部分,将数据分解成字节流,以便存储在文件中或在网络上传输.反序列