THIS IS B3c0me

记录生活中的点点滴滴

0%

Java序列化与反序列化

一、序列化和反序列化的概念

序列化(serialization):将java对象以一连串的字节保存在磁盘文件中的过程,也可以说是保存java对象状态的过程。序列化可以将数据永久保存在磁盘上(通常保存在文件中)。

**deserialization(反序列化)**:将保存在磁盘文件中的java字节码重新转换成java对象称为反序列化

Java序列化的优点

  • 实现了数据的持久化,通过序列化可以把数据持久地保存在硬盘上(磁盘文件)。
  • 利用序列化实现远程通信,在网络上传输字节序列。

二、Java序列化和反序列化的应用

两个进程在远程通信时,可以发送多种数据,包括文本、图片、音频、视频等,这些数据都是以二进制序列的形式在网络上传输。

java是面向对象的开发方式,一切都是java对象,想要在网络中传输java对象,可以使用序列化和反序列化去实现,发送方需要将java对象转换为字节序列,然后在网络上传送,接收方收到字符序列后,会通过反序列化将字节序列恢复成java对象。

三、序列化和反序列化的实现

JDK类库提供的序列化API:

  • java.io.ObjectOutputStream

    表示对象输出流,其中writeObject(Object obj)方法可以将给定参数的obj对象进行序列化,将转换的一连串的字节序列写到指定的目标输出流中。

  • java.io.ObjectInputStream

    该类表示对象输入流,该类下的readObject(Object obj)方法会从源输入流中读取字节序列,并将它反序列化为一个java对象并返回。

序列化的要求

实现序列化的类对象必须实现了Serializable类或Externalizable类才能被序列化,否则会抛出异常

实现序列化和反序列化的三种方法

假如现在要对Student类进行序列化和反序列化:

  • 方法一:若Student类实现了serializable接口,则可以通过objectOutputstream和objectinputstream默认的序列化和反序列化方式,对非transient的实例变量进行序列化和反序列化。

    1
    2
    注:将不需要序列化的属性前添加关键字transient
    序列化对象的时候,这个属性就不会被序列化
  • 方法二:若student类实现了serializable接口,并且定义了writeObject(objectOutputStream out)和

    readObject(objectinputStream in)方法,则可以直接调用student类的两种方法进行序列化和反序列化

  • 方法三:若student类实现了Externalizable接口,则必须实现readExternal(Objectinput in)和writeExternal(Objectoutput out)方法进行序列化和反序列化

JDK类库中的序列化步骤:

第一步:创建一个输出流对象,它可以包装一个输出流对象,如:文件输出流。

1
ObjectOutputStream out = new ObjectOutputStream(new fileOutputStream("E:\\JavaXuLiehua\\Student\\Student1.txt"));

第二步:通过输出流对象的writeObject()方法写对象

1
2
out.writeObject("hollo word");
out.writeObject("happy")

JDK类库中的反序列化步骤

第一步:创建文件输入流对象

1
ObjectInputStream in = new ObjectInputStream(new fileInputStream("E:\\JavaXuLiehua\\Student\\Student1.txt"));

第二步:调用readObject()方法

1
2
String obj1 = (String)in.readObject();
String obj2 = (String)in.readObject();

为了保证正确读取数据,对象输出流写入对象的顺序与对象输入流读取对象的顺序一致。

Student类序列化和反序列化演示

1.先创建一个实现了了Serializable接口的Student类

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package serializationTest;

import java.io.Serializable;

/**
* @Description :
* @Author: zcwww
* 2023/8/29 10:17
*/
public class Student implements Serializable {
private String name;
private char sex;
private int year;
private double gpa;

//无参构造器
public Student(){
}

//实参构造器
public Student(String name, char sex, int year, double gpa){
this.name = name;
this.sex = sex;
this.year = year;
this.gpa = gpa;
}

//getter
public String getName(){return name;}
public char getSex(){return sex;}
public int getYear(){return year;}
public double getGpa(){return gap;}

//setter
public void setName(String name){this.name = name;}
public void setSex(char sex){this.sex = sex;}
public void setYear(int year){this.year = year;}
public void setGpa(double gpa){this.gpa = gpa;}

}

2.把Student类的对象序列化到txt文件中,并实现反序列化

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class UserStudent{
public static void main(String[] args) throws IOException {
Student student = new Student("Tommy",'M', 2001, 3.6);
File file = new File("E:\\OneDrive\\桌面\\student1.txt");
if(file.exists()){
System.out.println("file exists");
} else {
boolean ifCreate = file.createNewFile();
if(ifCreate){
System.out.println("文件创建成功!");
} else {
System.out.println("文件创建失败!");
}
}
try{
//Student序列化
FileOutputStream fileOutputStream = new FileOutputStream(file);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
//调用objectOutPUstStream类的writeObject() 方法写对象
objectOutputStream.writeObject(student);
objectOutputStream.flush();
fileOutputStream.close();
objectOutputStream.close();

//Student反序列化
FileInputStream fileInputStream = new FileInputStream(file);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
//读取对象
Student student1 = (Student)objectInputStream.readObject(); //此处会抛出找不到类的异常
System.out.println("name:" + student1.getName());
System.out.println("sex:" + student1.getSex());
System.out.println("year:" +student1.getYear());
System.out.println("gpa:" + student1.getGpa());

objectInputStream.close();
fileInputStream.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}

3.运行UserStudent,发现已经成功读取到了反序列化得到的student对象

4.打开生成的student1.txt,查看里面的内容

可以看出该内容是不容易阅读的,只能通过反序列化读取

5.使用transient关键字修饰namesex属性,验证被transient修饰的单元不能被序列化:

1
2
private  transient String name;
private transient char sex;

重新运行上面的UserStudent,可以看到从反序列化的流中没有读取出student对象的name和sex属性,因为这两个属性没有被序列化

四、Externalizable接口实现序列化与反序列化

Externalizable接口继承Serializable接口,实现Externalizable接口需要实现readExternal()方法和writeExternal()方法,这两个方法是抽象方法,对应的是serializable接口的readObject()方法和writeObject()方法,可以理解为把serializable的两个方法抽象出来。Externalizable没有serializable的限制,static和transient关键字修饰的属性也能进行序列化

具体实现:

1.创建一个Student1类,其中name 和 sex 属性被transient关键字修饰,还要重写readExternalwriteExternal方法

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package serializationTest;

import java.io.*;

/**
* @Description :
* @Author: zcwww
* 2023/8/29 10:17
*/
public class Student1 implements Externalizable {
private transient String name;
private transient char sex;
private int year;
private double gpa;

//无参构造器
public Student1(){
}

//实参构造器
public Student1(String name, char sex, int year, double gpa){
this.name = name;
this.sex = sex;
this.year = year;
this.gpa = gpa;
}

//getter
public String getName(){return name;}
public char getSex(){return sex;}
public int getYear(){return year;}
public double getGpa(){return gpa;}

//setter
public void setName(String name){this.name = name;}
public void setSex(char sex){this.sex = sex;}
public void setYear(int year){this.year = year;}
public void setGpa(double gpa){this.gpa = gpa;}

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

@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = (String)in.readObject();
sex = (char)in.readObject();
year = (int)in.readObject();
gpa = (double)in.readObject();
}
}

2.测试方法:UserStudent2

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class UserStudent2{
public static void main(String[] args) throws IOException {
Student1 student = new Student1("Tommy",'M', 2001, 3.6);
File file = new File("E:\\OneDrive\\桌面\\student1.txt");
if(file.exists()){
System.out.println("file exists");
} else {
boolean ifCreate = file.createNewFile();
if(ifCreate){
System.out.println("文件创建成功!");
} else {
System.out.println("文件创建失败!");
}
}
try{
//Student序列化
FileOutputStream fileOutputStream = new FileOutputStream(file);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
//调用 ObjectOutputStream 中的 writeObject() 方法 写对象
objectOutputStream.writeObject(student); //会自动执行重写的writeExternal()方法
objectOutputStream.flush();
fileOutputStream.close();
objectOutputStream.close();

//Student反序列化
FileInputStream fileInputStream = new FileInputStream(file);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
//读取对象
//会自动执行readExternal()方法
Student1 student1 = (Student1) objectInputStream.readObject(); //会抛出异常(类找不到异常)
System.out.println("name:" + student1.getName());
System.out.println("sex:" + student1.getSex());
System.out.println("year:" +student1.getYear());
System.out.println("gpa:" + student1.getGpa());

objectInputStream.close();
fileInputStream.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}

相应的测试方法里面调用这两种方法的时候,直接调用writeObject()方法和readObject()方法即可,重写的writeExternal()和readExternal()方法会自动执行。

3.虽然student1类里的name和sex属性被static或transient修饰,但依旧被序列化,运行结果如下:

参考

1.https://blog.csdn.net/qq_62414755/article/details/125886742

欢迎关注我的其它发布渠道