IO流的序列化和反序列化

本文涉及的产品
系统运维管理,不限时长
简介: 序列化:指把堆内存中的 Java 对象数据,通过某种方式把对象存储到磁盘文件中或者传递给其他网络节点(在网络上传输)。这个过程称为序列化。通俗来说就是将数据结构或对象转换成二进制串的过程

何为序列化和反序列化


序列化:指把堆内存中的 Java 对象数据,通过某种方式把对象存储到磁盘文件中或者传递给其他网络节点(在网络上传输)。这个过程称为序列化。通俗来说就是将数据结构或对象转换成二进制串的过程

反序列化:把磁盘文件中的对象数据或者把网络节点上的对象数据,恢复成Java对象模型的过程。也就是将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程

好像有些说的不够清晰,我们缩短一下。

序列化:把对象转换为字节序列的过程称为对象的序列化。(常见的就是存文件)

反序列化:把字节序列恢复为对象的过程称为对象反序列化。

再通俗一点。

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

为何会诞生它们

我们想要学好一个东西,那就肯定必须要去了解它的作用和原因。

为了节省时间,我在博客园大佬博客里面找到了一个清晰明了的解释。

网络异常,图片无法展示
|

JavaBeanSerializeable

程序创建的每个JavaBean类都实现Serializeable接口。

不知道你们看到这句话的时候,有没有感到疑惑?嘿嘿,那就对了,这个你们可能没去了解过。

那我们现在就来讲一讲这两个玩意,不过,我这篇文章是讲解序列化的,所以我肯定不会去自己写一份说明,这样太费时间了,于是我将会截图知乎上的讲解或其他网上博主的说明。

但是,说的不明白的文章我肯定不会选择的。

我们看看知乎大佬的说明!

网络异常,图片无法展示
|

这就是我们所说的JavaBean的由来了。它或许你们可以完美理解成是一个概念,而非具体的东西。

而我们想要一个类可以实现JavaBean这个概念,我们需要有如下条件。

1、所有属性为private

2、提供默认构造方法

3、提供getter和setter

4、实现serializable接口

这里我们就发现了第二个疑问,serializable,这是啥?

这个玩意,是个接口,这个接口有什么用呢?

Serializable接口是一个标记接口,不用实现任何方法。一旦实现了此接口,该类的对象就是可序列化的

也就是说,他就是个标记一样,它没什么内容需要你实现,你继承了这个接口,就给了一个标记,有这个标记的类就可序列化。

不过同时也必须满足上面的四个条件才可以!!!

我们写一段试试。

示范

网络异常,图片无法展示
|

这就是一个满足序列化的类,这个类我定义了两个字段,最后一个重写是返回了一个String值,他就和普通类作用一样,只不过需要满足一些条件。

package IoDemo.Demo;
import java.io.Serializable;
public class IoObj implements Serializable {
    private String str;
    private int num;
    //默认构造
    public IoObj() {
    }
    public IoObj(String str, int num) {
        this.str = str;
        this.num = num;
    }
    public String getStr() {
        return str;
    }
    public void setStr(String str) {
        this.str = str;
    }
    public int getNum() {
        return num;
    }
    public void setNum(int num) {
        this.num = num;
    }
    @Override
    public String toString() {
        return "IoObj [str=" + str + ", num=" + num + "]";
    }
}

如何序列化

好了,这里有一个支持序列化的类了,我们现在就来试试如何将这个类的对象序列化。

  • 步骤一:创建一个ObjectOutputStream输出流;
  • 步骤二:调用ObjectOutputStream对象的writeObject输出可序列化对象。

Object是对象的意思,我们这里可以的序列化与反序列化又可以叫对象流

我们先看代码:

package IoDemo.Demo;
import java.io.*;
public class IoDemoTest {
    public static void main(String[] args) {
        IoObj ioObj = new IoObj("序列化测试",1);
        // 创建Object流对象
        ObjectOutputStream oos = null;
        try {
            // 创建文件对象
            File file = new File("D:\\test.txt");
            // 创建文件输出流对象
            FileOutputStream fos = new FileOutputStream(file);
            // 创建Object流对象
            oos = new ObjectOutputStream(fos);
            // 写入对象
            oos.writeObject(ioObj);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

这里我用了几步呢?

新建对象

IoObj ioObj = new IoObj("序列化测试",1);

写入文件的位置

我们想要将对象写入到D:\\test.txt,于是我们需要用File对象保存地址。

然后我们还需要将我们序列化的内容写入到文件,所以我们还得创建文件的输出流。

// 创建文件对象
File file = new File("D:\\test.txt");
// 创建文件输出流对象
FileOutputStream fos = new FileOutputStream(file);

序列化(创建对象流对象)

// 创建Object流对象
oos = new ObjectOutputStream(fos);

这里我们将上面的输出流对象给了对象流对象。

然后对象流对象有一个方法,是writeObject()方法,用于写入对象。

// 写入对象
oos.writeObject(ioObj);

这样,我们就将IoObj对象写入到了文件,我们看一下。

网络异常,图片无法展示
|

注意:当序列化一个对象到文件时, 按照 Java 的标准约定是给文件一个 .ser 扩展名。

至于,你可能疑惑,这写入的内容是什么?看不懂。那看看下面的解释就明白了。

网络异常,图片无法展示
|

反序列化

网络异常,图片无法展示
|

package IoDemo.Demo;
import java.io.*;
public class IoDemoTest {
    public static void main(String[] args) {
        IoObj ioObj = new IoObj("序列化测试",1);
        // 创建Object流对象
        ObjectOutputStream oos = null;
        try {
            // 创建文件对象
            File file = new File("D:\\test.txt");
            //读取文件
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
            // 读取对象
            IoObj ioObj1 = (IoObj) ois.readObject();
            System.out.println(ioObj1.toString());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

反序列化同理可知了,我就不多说了。

ObjectInputStream是对象的写入流。

// 读取对象
IoObj ioObj1 = (IoObj) ois.readObject();

这里是将读取的值赋值给对象,readObject()方法就是用于读取对象流文件内容。

至于(IoObj),你可以理解为强转。

扩展

这儿有一位博客园大佬,我也借鉴了它文章,大家可以看看。

大佬的博客

文章有许多扩展知识点,我就直接为了方便阅读,一起搬过来,版权链接上面给了。

反序列化并不会调用构造方法。反序列的对象是由JVM自己生成的对象,不通过构造方法生成。

成员是引用的序列化

如果一个可序列化的类的成员不是基本类型,也不是String类型,那这个引用类型也必须是可序列化的;否则,会导致此类不能序列化。

如存在这种成员:private Person person

同一对象序列化多次的机制

同一对象序列化多次,会将这个对象序列化多次吗?答案是否定的。Java序列化同一对象,并不会将此对象序列化多次得到多个对象。

Java序列化算法
  1. 所有保存到磁盘的对象都有一个序列化编码号
  2. 当程序试图序列化一个对象时,会先检查此对象是否已经序列化过,只有此对象从未(在此虚拟机)被序列化过,才会将此对象序列化为字节序列输出。
  3. 如果此对象已经序列化过,则直接输出编号即可。

网络异常,图片无法展示
|

Java序列化算法潜在的问题

由于Java序列化算法不会重复序列化同一个对象,只会记录已序列化对象的编号。如果序列化一个可变对象(对象内的内容可更改)后,更改了对象内容,再次序列化,并不会再次将此对象转换为字节序列,而只是保存序列化编号。

可选的自定义序列化

有些时候,我们有这样的需求,某些属性不需要序列化。使用transient关键字选择不需要序列化的字段。

public class Person implements Serializable {
   //不需要序列化名字与年龄
   private transient String name;
   private transient int age;
   private int height;
   private transient boolean singlehood;
   public Person(String name, int age) {
       this.name = name;
       this.age = age;
   }
   //省略get,set方法
}
public class TransientTest {
   public static void main(String[] args) throws Exception {
       try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"));
            ObjectInputStream ios = new ObjectInputStream(new FileInputStream("person.txt"))) {
           Person person = new Person("9龙", 23);
           person.setHeight(185);
           System.out.println(person);
           oos.writeObject(person);
           Person p1 = (Person)ios.readObject();
           System.out.println(p1);
       }
   }
}
//输出结果
//Person{name='9龙', age=23', singlehood=true', height=185cm}
//Person{name='null', age=0', singlehood=false', height=185cm}

从输出我们看到,使用transient修饰的属性,Java序列化时,会忽略掉此字段,所以反序列化出的对象,被transient修饰的属性是默认值。对于引用类型,值是null;基本类型,值是0;boolean类型,值是false。

两种序列化对比
实现Serializable接口 实现Externalizable接口
系统自动存储必要的信息 程序员决定存储哪些信息
Java内建支持,易于实现,只需要实现该接口即可,无需任何代码支持 必须实现接口内的两个方法
性能略差 性能略好

虽然Externalizable接口带来了一定的性能提升,但变成复杂度也提高了,所以一般通过实现Serializable接口进行序列化。

总结


  1. 所有需要网络传输的对象都需要实现序列化接口,通过建议所有的javaBean都实现Serializable接口。
  2. 对象的类名、实例变量(包括基本类型,数组,对其他对象的引用)都会被序列化;方法、类变量、transient实例变量都不会被序列化。
  3. 如果想让某个变量不被序列化,使用transient修饰。
  4. 序列化对象的引用类型成员变量,也必须是可序列化的,否则,会报错。
  5. 反序列化时必须有序列化对象的class文件。
  6. 当通过文件、网络来读取序列化后的对象时,必须按照实际写入的顺序读取。
  7. 单例类序列化,需要重写readResolve()方法;否则会破坏单例原则。
  8. 同一对象序列化多次,只有第一次序列化为二进制流,以后都只是保存序列化编号,不会重复序列化。
  9. 建议所有可序列化的类加上serialVersionUID版本号,方便项目升级。
相关文章
|
3月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
2天前
|
JSON 数据格式 索引
Python中序列化/反序列化JSON格式的数据
【11月更文挑战第4天】本文介绍了 Python 中使用 `json` 模块进行序列化和反序列化的操作。序列化是指将 Python 对象(如字典、列表)转换为 JSON 字符串,主要使用 `json.dumps` 方法。示例包括基本的字典和列表序列化,以及自定义类的序列化。反序列化则是将 JSON 字符串转换回 Python 对象,使用 `json.loads` 方法。文中还提供了具体的代码示例,展示了如何处理不同类型的 Python 对象。
|
13天前
|
存储 安全 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第22天】在Java的世界里,对象序列化和反序列化是数据持久化和网络传输的关键技术。本文将带你了解如何在Java中实现对象的序列化与反序列化,并探讨其背后的原理。通过实际代码示例,我们将一步步展示如何将复杂数据结构转换为字节流,以及如何将这些字节流还原为Java对象。文章还将讨论在使用序列化时应注意的安全性问题,以确保你的应用程序既高效又安全。
|
2月前
|
存储 Java
序列化流 ObjectInputStream 和 ObjectOutputStream 的基本使用【 File类+IO流知识回顾④】
这篇文章介绍了Java中ObjectInputStream和ObjectOutputStream类的基本使用,这两个类用于实现对象的序列化和反序列化。文章解释了序列化的概念、如何通过实现Serializable接口来实现序列化,以及如何使用transient关键字标记不需要序列化的属性。接着,通过示例代码演示了如何使用ObjectOutputStream进行对象的序列化和ObjectInputStream进行反序列化。
序列化流 ObjectInputStream 和 ObjectOutputStream 的基本使用【 File类+IO流知识回顾④】
|
26天前
|
存储 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第9天】在Java的世界里,对象序列化是连接数据持久化与网络通信的桥梁。本文将深入探讨Java对象序列化的机制、实践方法及反序列化过程,通过代码示例揭示其背后的原理。从基础概念到高级应用,我们将一步步揭开序列化技术的神秘面纱,让读者能够掌握这一强大工具,以应对数据存储和传输的挑战。
|
1月前
|
存储 安全 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第3天】在Java编程的世界里,对象序列化与反序列化是实现数据持久化和网络传输的关键技术。本文将深入探讨Java序列化的原理、应用场景以及如何通过代码示例实现对象的序列化与反序列化过程。从基础概念到实践操作,我们将一步步揭示这一技术的魅力所在。
|
12天前
|
存储 缓存 NoSQL
一篇搞懂!Java对象序列化与反序列化的底层逻辑
本文介绍了Java中的序列化与反序列化,包括基本概念、应用场景、实现方式及注意事项。序列化是将对象转换为字节流,便于存储和传输;反序列化则是将字节流还原为对象。文中详细讲解了实现序列化的步骤,以及常见的反序列化失败原因和最佳实践。通过实例和代码示例,帮助读者更好地理解和应用这一重要技术。
10 0
|
2月前
|
存储 XML JSON
用示例说明序列化和反序列化
用示例说明序列化和反序列化
|
2月前
|
JSON fastjson Java
niubility!即使JavaBean没有默认无参构造器,fastjson也可以反序列化。- - - - 阿里Fastjson反序列化源码分析
本文详细分析了 Fastjson 反序列化对象的源码(版本 fastjson-1.2.60),揭示了即使 JavaBean 沲有默认无参构造器,Fastjson 仍能正常反序列化的技术内幕。文章通过案例展示了 Fastjson 在不同构造器情况下的行为,并深入探讨了 `ParserConfig#getDeserializer` 方法的核心逻辑。此外,还介绍了 ASM 字节码技术的应用及其在反序列化过程中的角色。
69 10
|
2月前
|
存储 Java 开发者
Java编程中的对象序列化与反序列化
【9月更文挑战第20天】在本文中,我们将探索Java编程中的一个核心概念——对象序列化与反序列化。通过简单易懂的语言和直观的代码示例,你将学会如何将对象状态保存为字节流,以及如何从字节流恢复对象状态。这不仅有助于理解Java中的I/O机制,还能提升你的数据持久化能力。准备好让你的Java技能更上一层楼了吗?让我们开始吧!