Java序列化与反序列化实战详解
一,Java序列化与反序列化
1.Serialization(序列化):
Java序列化是指将Java对象转换为字节流的过程,以便将对象的状态保存到文件、数据库或通过网络传输。序列化后的字节流包含对象的数据及其类型信息,使得对象可以在不同JVM或环境中重建。
2.deserialization(反序列化):
Java反序列化是指将序列化后的字节流重新转换为Java对象的过程,恢复对象原有的状态和数据。反序列化通过读取字节流并重建对象的内存结构来实现。
二,序列化以及反序列化的应用
- 对象持久化(保存到文件)
- 网络传输(RPC、Socket)
- 缓存(Redis 存储对象)
- 消息队列(RabbitMQ、RocketMQ 传输对象)
- 分布式 Session 共享
Java序列化的优点:
- 便于对象持久化,可以将对象保存到文件或数据库。
- 支持网络传输,方便对象在不同系统之间传递。
- 适用于分布式系统,实现远程调用、Session 共享等。
- 实现简单,对象实现
Serializable接口即可进行序列化。
三,序列化与反序列化的实现
1.向文件中写入字符串
序列化操作:
首先是创建这个字节输出流来指向你所要操作的文件,然后创建OutputStreamWriter将字节流转换为字符流,并按照utf-8的字符集进行写入,然后再加一个BufferWriter来增加一个缓冲区来增加写入效率,使用这个write方法进行写入字符串,然后调用flush方法,强制将缓冲区中的数据立即写入文件,最后使用close方法关闭流并且释放资源。
@Test
public void test_out_serialization() {
try {
FileOutputStream fileOutputStream = new FileOutputStream("D:\\Java\\Java-demo\\src\\test\\resources\\Student1.txt");
OutputStreamWriter outputStreamWriter=new OutputStreamWriter(fileOutputStream,StandardCharsets.UTF_8);
BufferedWriter bufferedWriter=new BufferedWriter(outputStreamWriter);
bufferedWriter.write("hello world1");
bufferedWriter.flush();
bufferedWriter.close();
}catch (IOException e){
System.out.println(e.getMessage());
}
System.out.println("ok");
}
写入后的效果:


反序列化操作:
首先是创建这个字节输入流来指向你所要操作的文件,然后创建InputStreamReader将字节流转换为字符流,并按照utf-8的字符集进行写入,然后再加一个BufferReader来增加一个缓冲区来增加读取效率,使用这个readLine方法进行读取一行字符串,最后使用close方法关闭流并且释放资源。
@Test
public void test_in_serialization() {
try {
FileInputStream fileInputStream = new FileInputStream("D:\\Java\\Java-demo\\src\\test\\resources\\Student1.txt");
InputStreamReader inputStreamReader =new InputStreamReader(fileInputStream,StandardCharsets.UTF_8);
BufferedReader bufferedReader=new BufferedReader(inputStreamReader);
String read = bufferedReader.readLine();
bufferedReader.close();
System.out.println(read);
}catch (IOException e){
System.out.println(e.getMessage());
}
System.out.println("ok");
}
读取后的效果:

2.向文件中写入对象
首先要创建一个实体类,然后这个类要实现Serializable接口
package com.cat.entity;
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;
}
//重写set和get
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public double getGpa() {
return gpa;
}
public void setGpa(double gpa) {
this.gpa = gpa;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex=" + sex +
", year=" + year +
", gpa=" + gpa +
'}';
}
}
序列化操作
首先是创建一个实体类对象,然后再创建这个字节输出流来指向你所要操作的文件,然后再创建一个ObjectOutputStream对象输出流将Java对象转换为字节流,使用这个writeObject方法进行写入对象,然后调用flush方法,强制将缓冲区中的数据立即写入文件,最后使用close方法关闭流并且释放资源。
@Test
public void test_out_object_serialization() throws IOException {
Student student=new Student("cat",'M',21,3.6);
File file = new File("D:\\Java\\Java-demo\\src\\test\\resources\\Student1.txt");
if (file.exists()){
System.out.println("文件存在");
}else {
file.createNewFile();
}
try {
FileOutputStream fos=new FileOutputStream(file);
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(student);
oos.flush();
fos.close();
oos.close();
}catch (IOException e){
System.out.println(e.getMessage());
}
System.out.println("ok");
}
写入后的效果:


从这里可以看出序列化的内容是很难读取的,是要靠反序列化进行读取的
反序列化操作:
首先创建这个字节输入流来指向你所要操作的文件,然后再创建一个ObjectInputStream对象输入流将字节流转换为Java对象,使用这个readObject方法进行写入对象,然后调用flush方法,强制将缓冲区中的数据立即写入文件,最后使用close方法关闭流并且释放资源。
@Test
public void test_in_object_serialization() throws IOException {
File file = new File("D:\\Java\\Java-demo\\src\\test\\resources\\Student1.txt");
if (file.exists()){
System.out.println("文件存在");
}else {
file.createNewFile();
}
try {
FileInputStream fis=new FileInputStream(file);
ObjectInputStream ois=new ObjectInputStream(fis);
Student student = (Student) ois.readObject();
System.out.println(student);
ois.close();
fis.close();
}catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
System.out.println("ok");
}
结果如下:

四,transient关键字
被transient关键字修饰的属性,不能被序列化

这里给sex属性加了transient关键字,这里要注意序列化的实体类发生了改变,要将文件中的内容删除,重新序列化一遍这个实体类才可以,要不然反序列化时会报错

可以看到这时sex字段的值变为空值,没有被序列化
五,Externalizable接口实现序列化与反序列化
Externalizable接口继承Serializable接口,实现Externalizable接口需要实现readExternal()方法和writeExternal()方法,这两个方法是抽象方法,对应的是serializable接口的readObject()方法和writeObject()方法,可以理解为把serializable的两个方法抽象出来。Externalizable没有serializable的限制,static和transient关键字修饰的属性也能进行序列化。
在实体类中重写这两个方法
@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();
}
相应的测试方法里面调用这两种方法的时候,直接调用writeObject()方法和readObject()方法即可,重写的writeExternal()和readExternal()方法会自动执行。
FileOutputStream fos1 = new FileOutputStream(file1);
ObjectOutputStream oos1 = new ObjectOutputStream(fos1);
//调用 ObjectOutputStream 中的 writeObject() 方法 写对象
oos1.writeObject(st); //会自动执行重写的writeExternal()方法
FileInputStream fis1 = new FileInputStream(file1);
//创建对象输入流
ObjectInputStream ois1 = new ObjectInputStream(fis1);
//读取对象
//会自动执行readExternal()方法
Student1 st1 = (Student1) ois1.readObject(); //会抛出异常(类找不到异常)
虽然student类里的sex属性被static或transient修饰,但依旧被序列化,结果如下:
文件存在
name = cat
sex = M
year = 20
gpa = 3.6
更多推荐


所有评论(0)