序列化和反序列化

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

  • 序列化过程:是指把一个Java对象变成二进制内容,实质上就是byte[]。因为序列化后可以把byte[]保存到文件中,或者把byte[]通过网络传输到远程(IO),如此就相当于把java存储到文件或者通过网络传输出去了

 

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

  • 反序列化过程:把一个二进制内容(也就是byte[])变回Java对象。有了反序列化,保存到文件中的byte[]又可以“变成”java对象,或者从网络上读取byte[]并把它“变成”Java对象。

 

三、为什么需要序列化

当两个进程进行远程通信时,可以互相发送各种类型的数据(包括文本、图片、音频、视频等),而这些数据都会以二进制序列的形式在网络上传送。当两个Java进程进行通信时,需要Java序列化和反序列化实现进程间的对象传送,换句话说,一方面,发送方需要把这个 Java 对象转换为字节序列,然后在网络上传送;另一方面,接收方需要从字节序列中恢复出 Java 对象。

作用:

  1. 实现了数据的持久化,序列化可以把数据永久地保存在硬盘上(通常存放在文件里)

  2. 通过序列化以字节流的形式使对象在网络中进行传递和接收

  3. 通过序列化在进程间传递对象

注意事项:

1、某个类可以被序列化,则其子类也可以被序列化。

2、声明为static和transient的成员变量,不能被序列化。static成员变量是描述类级别的属性,transient表示临时数据

3、一个 Java 对象要能序列化,必须实现java.io.Serializable接口。因为序列化是一个非常复杂的算法操作,所以java官方给我们写好了,但是使用的方式很特殊,java底层写好了序列化操作,但是官方并不确定”你的Java对象”是否需要序列化,因此官方搞了一个”接口标记”的操作。如果对象需要底层的序列化操作,就实现Serializable 接口,如果对象不需要底层的序列化操作,就不需要实现Serializable 接口

  Serializable 接口源码:

public interface Serializable {
}

  Serializable 没有定义任何方法,它是一个空接口。这样的空接口称为“标记接口”(Marker Interface),其存在的意义就是为了添加标记

 

四、序列化方式

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

  •  ObjectOutputStream.writeObject(object):将可序列化的对象写入输出流
  •  ObjectInputStream.readObject():从输入流读取,构造并返回一个对象
package com.chenly.serialize;
import com.chenly.serialize.bean.Score;
import java.io.*;

/** * @author: chenly * @date: 2022-11-25 17:14 * @description:
*/ public class Test { public static void main(String[] args) { Score object = Score.builder().className("一班") .stuName("张三").course("生物").score(90).build(); ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectInputStream objectInputStream = null; ObjectOutputStream objectOutputStream = null; byte[] bytes; try { //序列化 objectOutputStream = new ObjectOutputStream(out); objectOutputStream.writeObject(object); bytes = out.toByteArray(); //反序列化 InputStream in = new ByteArrayInputStream(bytes); objectInputStream = new ObjectInputStream(in); Object obj = objectInputStream.readObject(); System.out.println("反序列化输出:" + obj); } catch (Exception e) { e.printStackTrace(); } finally { try { if (out != null) { out.close(); } if (objectInputStream != null) { objectInputStream.close(); } if (objectOutputStream != null) { objectOutputStream.close(); } } catch (IOException e) { e.printStackTrace(); } } } }

 

五、serialVersionUID常数

  • serialVersionUID是一个常数,用于标识可序列化类的版本,JVM在反序列化过程中检查此常数,如果正在反序列化的对象的serialVersionUID与类中指定的序列号不同,则JVM抛出InvalidClassException。
  • serialVersionUID 是可选的,如果不显式声明,Java编译器将自动生成一个,自动生成的serialVersionUID 是基于类的元素(成员变量、方法和构造函数等)计算的。如果这些元素一旦发生更改,serialVersionUID 也将更改。

  例如:定义了一个Student类,未显示声明serialVersionUID ,序列化后保存到文件中,之后在Student类中新增一个字段address,此时将文件中的Student对象反序列化就会抛异常,无法反序列化回来了。如果定义了serialVersionUID ,即使在Student里加了一个属性,但还是可以反序列化回来的,只是新加的那个属性值为null而已

 

posted @ 2022-11-28 17:34  harara  阅读(183)  评论(0)    收藏  举报