一、序列化和反序列化的概念
序列化(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;
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; }
public String getName(){return name;} public char getSex(){return sex;} public int getYear(){return year;} public double getGpa(){return gap;}
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{ FileOutputStream fileOutputStream = new FileOutputStream(file); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(student); objectOutputStream.flush(); fileOutputStream.close(); objectOutputStream.close();
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关键字修饰name
和sex
属性,验证被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关键字修饰,还要重写readExternal
和writeExternal
方法
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.*;
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; }
public String getName(){return name;} public char getSex(){return sex;} public int getYear(){return year;} public double getGpa(){return gpa;}
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{ FileOutputStream fileOutputStream = new FileOutputStream(file); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(student); objectOutputStream.flush(); fileOutputStream.close(); objectOutputStream.close();
FileInputStream fileInputStream = new FileInputStream(file); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); 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