什么是序列化

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

什么是反序列化

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

序列化的实现

当 Java 对象需要在网络上传输 或者 持久化存储到文件中时,就需要对 Java 对象进行序列化处理。

序列化的实现:类实现 Serializable 接口,这个接口没有需要实现的方法。实现 Serializable 接口是为了告诉 jvm 这个类的对象可以被序列化。

注意事项:

  • 某个类可以被序列化,则其子类也可以被序列化
  • 声明为 static 和 transient 的成员变量,不能被序列化。static 成员变量是描述类级别的属性,transient 表示临时数据
  • 反序列化读取序列化对象的顺序要保持一致

为什么要进行Java序列化

序列化过程:

是指把一个Java对象变成二进制内容,实质上就是一个byte[]数组。

因为序列化后可以把byte[]保存到文件中,或者把byte[]通过网络传输到远程(IO),这样,就相当于把Java对象存储到文件或者通过网络传输出去了。

反序列化过程:

  • 把一个二进制内容(也就是byte[]数组)变回Java对象。有了反序列化,保存到文件中的byte[]数组又可以“变回”Java对象,或者从网络上读取byte[]并把它“变回”Java对象。
  • 以下是一些使用序列化的示例:
  • -以面向对象的方式将数据存储到磁盘上的文件,例如,Redis存储Student对象的列表。
  • -将程序的状态保存在磁盘上,例如,保存游戏状态。
  • -通过网络以表单对象形式发送数据,例如,在聊天应用程序中以对象形式发送消息。

一个Java对象要能序列化,必须实现一个特殊的java.io.Serializable接口,它的定义如下:

public interface Serializable {
}

Serializable接口没有定义任何方法,它是一个空接口。我们把这样的空接口称为“标记接口”(Marker Interface),实现了标记接口的类仅仅是给自身贴了个“标记”,并没有增加任何方法。

Java中的序列化如何工作

当且仅当对象的类实现java.io.Serializable 接口时,该对象才有资格进行序列化。可序列化 是一个标记接口(不包含任何方法),该接口告诉Java虚拟机(JVM)该类的对象已准备好写入持久性存储或通过网络进行读取。

默认情况下,JVM负责编写和读取可序列化对象的过程。序列化/反序列化功能通过对象流类的以下两种方法公开:

ObjectOutputStream。writeObject(Object):将可序列化的对象写入输出流。如果要序列化的某些对象未实现Serializable接口,则此方法将引发NotSerializableException。

ObjectInputStream。readObject():从输入流读取,构造并返回一个对象。如果找不到序列化对象的类,则此方法将引发ClassNotFoundException。

如果序列化使用的类有问题,则这两种方法都将引发InvalidClassException,如果发生I / O错误,则将引发IOException。无论NotSerializableException和InvalidClassException是子类IOException异常。

让我们来看一个简单的例子。以下代码将String对象序列化为名为“ data.ser”的文件。字符串对象是可序列化的,因为String类实现了Serializable 接口:

String filePath = "data.ser";
String message = "Java Serialization is Cool";

try (
FileOutputStream fos = new FileOutputStream(filePath);
ObjectOutputStream outputStream = new ObjectOutputStream(fos);
) {

outputStream.writeObject(message);

} catch (IOException ex) {
System.err.println(ex);
}


以下代码反序列化文件“ data.ser”中的String对象:
String filePath = "data.ser";

try (
FileInputStream fis = new FileInputStream(filePath);
ObjectInputStream inputStream = new ObjectInputStream(fis);
) {

String message = (String) inputStream.readObject();

System.out.println("Message: " + message);

} catch (ClassNotFoundException ex) {
System.err.println("Class not found: " + ex);
} catch (IOException ex) {
System.err.println("IO error: " + ex);
}

请注意,readObject()返回一个Object类型的对象,因此您需要将其强制转换为可序列化的类,在这种情况下为String类。

让我们看一个涉及使用自定义类的更复杂的示例。

给定以下学生班:

import java.io.*;
import java.util.*;

/**
* Student.java
* @author chenhh
*/
public class Student extends Person implements Serializable {
public static final long serialVersionUID = 1234L;

private long studentId;
private String name;
private transient int age;

public Student(long studentId, String name, int age) {
super();
this.studentId = studentId;
this.name = name;
this.age = age;

System.out.println("Constructor");
}

public String toString() {
return String.format("%d - %s - %d", studentId, name, age);
}
}

如上面代码,你会发现两点:

-long serialVersionUID类型的常量。

-成员变量age被标记为transient。

下面让我解释一下它们。

什么是serialVersionUID常数

serialVersionUID是一个常数,用于唯一标识可序列化类的版本。从输入流构造对象时,JVM在反序列化过程中检查此常数。如果正在读取的对象的serialVersionUID与类中指定的序列号不同,则JVM抛出InvalidClassException。这是为了确保正在构造的对象与具有相同serialVersionUID的类兼容。

请注意,serialVersionUID是可选的。这意味着如果您不显式声明Java编译器,它将生成一个。

那么,为什么要显式声明serialVersionUID呢?

原因是:自动生成的serialVersionUID是基于类的元素(成员变量,方法,构造函数等)计算的。如果这些元素之一发生更改,serialVersionUID也将更改。想象一下这种情况:

-您编写了一个程序,将Student类的某些对象存储到文件中。Student类没有显式声明的serialVersionUID。

-有时,您更新了Student类(例如,添加了一个新的私有方法),现在自动生成的serialVersionUID也被更改了。

-您的程序无法反序列化先前编写的Student对象,因为那里的serialVersionUID不同。JVM抛出InvalidClassException。

这就是为什么建议为可序列化类显式添加serialVersionUID的原因。

什么是瞬时变量?

在上面的Student类中,您看到成员变量age被标记为transient,对吗?JVM 在序列化过程中跳过瞬态变量。这意味着在序列化对象时不会存储age变量的值。

因此,如果成员变量不需要序列化,则可以将其标记为瞬态。

以下代码将Student对象序列化为名为“ students.ser”的文件:

String filePath = "students.ser";
Student student = new Student(123, "John", 22);

try (
FileOutputStream fos = new FileOutputStream(filePath);
ObjectOutputStream outputStream = new ObjectOutputStream(fos);
) {

outputStream.writeObject(student);

} catch (IOException ex) {
System.err.println(ex);
}

请注意,在序列化对象之前,变量age的值为22。

下面的代码从文件中反序列化Student对象:

String filePath = "students.ser";

try (
FileInputStream fis = new FileInputStream(filePath);
ObjectInputStream inputStream = new ObjectInputStream(fis);
) {

Student student = (Student) inputStream.readObject();

System.out.println(student);

} catch (ClassNotFoundException ex) {
System.err.println("Class not found: " + ex);
} catch (IOException ex) {
System.err.println("IO error: " + ex);
}

此代码将输出以下输出:

1个

123 - John - 0

有关Java序列化的更多信息

你应该了解一些有关序列化的重要信息:

  • 序列化一个对象时,它所引用的所有其他对象也会被序列化,依此类推,直到序列化完整的对象树为止。
  • 如果超类实现Serializable,则其子类会自动执行。
  • 反序列化可序列化类的实例时,构造函数将不会运行。
  • 如果超类未实现Serializable,则在反序列化子类对象时,超类构造函数将运行。
  • 静态变量未序列化,因为它们不是对象本身的一部分。
  • 如果序列化集合或数组,则每个元素都必须可序列化。单个不可序列化的元素将导致序列化失败(NotSerializableException)。
  • JDK中的可序列化类包括原始包装器(Integer,Long,Double等),String,Date,collection类…对于其他类,请查阅相关的Javadoc来了解它们是否可序列化。

带你了解Java的序列化与反序列化的更多相关文章

  1. java 对象序列化与反序列化

    Java序列化与反序列化是什么? 为什么需要序列化与反序列化? 如何实现Java序列化与反序列化? 本文围绕这些问题进行了探讨. 1.Java序列化与反序列化  Java序列化是指把Java对象转换为 ...

  2. Java对象序列化与反序列化一 JSON

    Java对象序列化与反序列化一 JSON 1. 依赖库 jackson-all-1.6.1.jar 2. 代码 public class Student {    private String nam ...

  3. Java对象序列化和反序列化的工具方法

    import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import ja ...

  4. Java之序列化和反序列化

    序列化的对象: package test_demo.SerializableOper; import java.io.Serializable; /* * 序列化对象需要实现序列号接口 * */ pu ...

  5. java之序列化与反序列化

    1.这里主要是介绍Protobuf提供的序列化与反序列化的高效性.相对于传统的java提供的序列化来说,Protobuf的效率提高了很多倍.但是也有不足的地方,就是proto在对象序列化的时候抛弃了很 ...

  6. Java基础—序列化与反序列化(转载)

    转载自: Java序列化与反序列化 1.Java序列化与反序列化 Java序列化是指把Java对象转换为字节序列的过程:而Java反序列化是指把字节序列恢复为Java对象的过程. 2.为什么需要序列化 ...

  7. Java 中序列化与反序列化

    一. 序列化和反序列化概念 Serialization(序列化)是一种将对象以一连串的字节描述的过程:反序列化deserialization是一种将这些字节重建成一个对象的过程.将程序中的对象,放入文 ...

  8. 深入分析Java的序列化与反序列化

    序列化是一种对象持久化的手段.普遍应用在网络传输.RMI等场景中.本文通过分析ArrayList的序列化来介绍Java序列化的相关内容.主要涉及到以下几个问题: 怎么实现Java的序列化 为什么实现了 ...

  9. java基础( 九)-----深入分析Java的序列化与反序列化

    序列化是一种对象持久化的手段.普遍应用在网络传输.RMI等场景中.本文通过分析ArrayList的序列化来介绍Java序列化的相关内容.主要涉及到以下几个问题: 怎么实现Java的序列化 为什么实现了 ...

随机推荐

  1. Ubuntu20.04连接WiFi

    电脑安装了Ubuntu20.04后发现没办法连接WiFi,也找不到WiFi图标,一般来说是因为Ubuntu系统没有网卡驱动,安装一下即可 解决办法如下: 先用网线或者手机开热点连接到到电脑,让电脑有网 ...

  2. COM组件对象模型基础

    COM组件对象模型 COM组件对象模型是为了创建一种独立于任何编程语言的对象.COM对象提供统一的接口,在不同的编程环境中通过调用COM对象特定接口的方法来完成特定的任务.一般有三种方式编写COM组件 ...

  3. 运行程序显示丢失“MSVCR100D.dll”

    前言 写了一个Dll注入工具,结果发现程序在其他机器上会出现丢失"MSVCR100D.dll".这个dll是vs2010自带的动态链接库,如果在没安装vs2010运行库的电脑中使用 ...

  4. MySQL锁等待与死锁问题分析

    前言: 在 MySQL 运维过程中,锁等待和死锁问题是令各位 DBA 及开发同学非常头痛的事.出现此类问题会造成业务回滚.卡顿等故障,特别是业务繁忙的系统,出现死锁问题后影响会更严重.本篇文章我们一起 ...

  5. Flink去重统计-基于自定义布隆过滤器

    一.背景说明 在Flink中对流数据进行去重计算是常有操作,如流量域对独立访客之类的统计,去重思路一般有三个: 基于Hashset来实现去重 数据存在内存,容量小,服务重启会丢失. 使用状态编程Val ...

  6. Python正则表达式的七个使用范例

    本文由 伯乐在线 - 左手的灵魂 翻译.未经许可,禁止转载!英文出处:thegeekstuff.欢迎加入翻译组.http://blog.jobbole.com/74844/ 作为一个概念而言,正则表达 ...

  7. OC与Swift混编,三种场景的实现方式

    多语言并存时期,混编成为一种必须的方式 ,在多场影中实现OC和Swift语言的并存原来是如此简单 第一种场景,App中实现混编 创建桥接文件*.h 新建一个桥接文件,New File 选择 Heade ...

  8. [转载]centos 7(1611)安装笔记

    centos 7(1611)安装笔记   麻烦 前天我把双系统笔记本里的 deepin 的磁盘分区直接从 Windows 7 磁盘管理里格式化了,结果悲催了,开不了机了,显示: 我以为是 Window ...

  9. JNLP配置WIN10

    依据网友的反响和我自己的考查,发觉JNLP的情况是大多数网友的困惑之处,针对此问题小编给大总结整理了(关键此)的处理技巧,并对它进以加工改善,整理出JNLP的详细教程,只用你按照1:首先配置电脑的ja ...

  10. 基于端口划分vlan

    基于端口划分vlan 拓扑图 PC ip 配置 PC 5:192.168.1.5 PC 6:192.168.1.6 PC 7:192.168.1.7 PC 8: 192.168.1.8 交换机配置 创 ...