Java序列化与反序列化

GTL-JU Lv3

JAVA 序列化和反序列化

一、序列化与反序列化的简单概述

  在前面我们学习了php和python的序列化和反序列化,那么在java中序列化同样是将java对象转换为字节序列的过程,那么同样反序列化就是将字节序列重新恢复为对象的过程。

​ 那么为什么要进行序列化与反序列化呢?

​ 从两个方面来说:

​ 1、从创建的对象存在周期来看:通常java中被创建的对象的声明周期不会比JVM虚拟机的存在周期更长,JVM虚拟机运行结束后,他创建的对象也就消失了,那么如果我们想要在JVM虚拟机运行结束后调用之前存在的对象,那么我们就可以通过序列化机制将之前创建的对象储存起到磁盘中,这样我们不仅可以调用之前创建的对象,也能让对象在另一个JVM中运行(这个核心类似于我们前面学习的RMI机制)。

​ 2、从数据的传输来看:当两个进行进行远程通信时,相互传递图片,文字等数据时是以二进制序列进行传输的,那么两个java进程之间的对象进行传输时要如何传输呢?是通过序列化转换为字节序列在网络上面进行传输的,在通过反序列化进行java对象的恢复。

二、序列化实现

​ 在上面我们说对象会通过序列化转换为字节序列从而在网络上面传输,那么在学习JAVA序列化之前我们先了解一下JAVA的输入输出流,也就是 JAVA IO。

 java的IO流分为了文件IO流(FileInput/OutputStream)和对象IO流(ObjectInput/OutputStream) ,那么可以看出无论是文件io还是对象io都存在输入输出流。

​ 接下来我们分析一下流的传输过程:

​ 无论是输出流还是输出流,流的两端都是文件和运行的java程序,所以我们如果想要在他们之间实现传输,就要通过搭建一个通道实现流的传输。

这里以输出流简单分析一下。

我们对一个文件进行写入的操作,那么实质上是将在java程序中将流输出到指定文件中:

1
2
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("filename"));
oos.writeObject(obj);

简单分析一下:

ObjectOutputStream 是 Java 中的一个类,它提供了将 Java 对象写入 OutputStream 的功能。它用于序列化 Java 对象,即将它们转换为可以在网络上发送或存储在文件中的字节流。

FileOutputStream 是 Java 中的一个类,它提供了将字节写入文件的功能。它用于将字节写入文件,可以用于创建、打开和写入文件。

所以通过这两个java类最终实现了对象到流到文件的转换。

下面开始正式分析序列化:

首先我们要了解只有实现了Serializable或者Externalizable接口的类的对象才能被序列化为字节序列,不是的话则会抛出异常。

Serializable 接口是 Java 中的一个接口,它没有任何方法,只是用来标记一个类可以被序列化。如果一个类实现了 Serializable 接口,就意味着该类的对象可以被序列化为一个字节序列,以便在网络上发送或存储在文件中。

1
2
public interface Serializable {
}

下面我们通过代码来分析一下序列化的过程:

首先定义Animal类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Animal {
private String color;

public Animal() {//没有无参构造将会报错
System.out.println("调用 Animal 无参构造");
}

public Animal(String color) {
this.color = color;

System.out.println("调用 Animal 有 color 参数的构造");
}

@Override
public String toString() {
return "Animal{" +
"color='" + color + '\'' +
'}';
}
}

BlackCat 是 Animal 的子类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class BlackCat extends Animal implements Serializable {
private static final long serialVersionUID = 1L;
private String name;

public BlackCat() {
super();
System.out.println("调用黑猫的无参构造");
}

public BlackCat(String color, String name) {
super(color);
this.name = name;
System.out.println("调用黑猫有 color 参数的构造");
}

@Override
public String toString() {
return "BlackCat{" +
"name='" + name + '\'' +super.toString() +'\'' +
'}';
}
}

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class SuperMain {
private static final String FILE_PATH = "./super.bin";

public static void main(String[] args) throws Exception {
serializeAnimal();
deserializeAnimal();
}

private static void serializeAnimal() throws Exception {
BlackCat black = new BlackCat("black", "我是黑猫");
System.out.println("序列化前:"+black.toString());
System.out.println("=================开始序列化================");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FILE_PATH));
oos.writeObject(black);
oos.flush();
oos.close();
}

private static void deserializeAnimal() throws Exception {
System.out.println("=================开始反序列化================");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(FILE_PATH));
BlackCat black = (BlackCat) ois.readObject();
ois.close();
System.out.println(black);
}
}

哪我们来分析一下测试类:

首先我们来分析一下实现序列化的方法:

首先创建一个BlackCat的实例化对象,

然后我们主要分析下面一段代码:

1
2
3
4
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FILE_PATH));
oos.writeObject(black);
oos.flush();
oos.close();

上面我们了解了ObjectOutputStream和FileOutputStream方法。

所以这段代码最终实现了对象转化为字节流,然后字节流写入到指定文件中

flush() 是 Java 中的一个方法,用于刷新输出流并强制将所有缓冲的输出字节写入底层流中。它可以用于确保所有数据都已经写入输出流中,而不需要关闭流。

writeObject()ObjectOutputStream 类中的一个方法,用于将一个对象写入输出流中进行序列化。它可以将一个实现了 Serializable 接口的对象转换成一个字节序列,并将其写入输出流中。

1
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FILE_PATH));

就已经实现了序列化为什么还要是用writeObject()方法和close()方法:

在Java中,ObjectOutputStream和FileOutputStream类是用于序列化对象和写入文件的类。它们确实会对对象进行序列化操作并将其写入文件中。但是,即使已经将对象序列化并写入文件中,仍然需要使用writeObject()和flush()方法来确保数据已经完全写入文件中。

writeObject()方法将对象写入缓冲区,而不是直接写入文件。flush()方法则强制将缓冲区中的所有数据写入文件。如果不使用flush()方法,数据可能会留存在缓冲区中,并且可能不会被写入文件中。因此,如果希望确保数据已经完全写入文件中,需要在使用ObjectOutputStream和FileOutputStream类时调用writeObject()和flush()方法。

总之,ObjectOutputStream和FileOutputStream类确实执行序列化和写入文件的操作,但是为了确保数据已经完全写入文件中,你需要调用writeObject()和flush()方法。

然后下面我们来分析一下反序列化的实现:

1
2
3
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(FILE_PATH));
BlackCat black = (BlackCat) ois.readObject();
ois.close();

readObject()ObjectInputStream 类中的一个方法,用于从输入流中读取一个对象进行反序列化。它可以将一个字节序列转换成一个对象,并返回该对象。

这段代码使用 FileInputStream 从指定的文件路径 FILE_PATH 中创建一个输入流,然后将该输入流作为参数传递给 ObjectInputStream 的构造函数,创建一个 ObjectInputStream 对象 ois。这个 ObjectInputStream 对象可以用于读取从该文件中写入的序列化对象。

  • 标题: Java序列化与反序列化
  • 作者: GTL-JU
  • 创建于: 2023-04-21 10:32:29
  • 更新于: 2023-04-21 10:44:15
  • 链接: https://gtl-ju.github.io/2023/04/21/Java序列化与反序列化/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。