- 什么是序列化和反序列化
- 序列化和反序列化的使用
- 如何保证反序列化后对象地址一致
序列化和反序列化一直都是一种很模糊的概念,关于理论有位大佬这么说到:
把对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为对象的过程称为对象的反序列化。
参考博客:Java对象的序列化和反序列化
本篇博客对其不做过多的阐述,只是单纯的说明序列化和反序列化使用时需要注意的相关细节。
序列化和反序列化的使用Java为了保证一个具有序列化和反序列化的能力,对其提供了一个接口java.io.Serializable。
只需要保证一个类实现他,就能完成对应的序列化和反序列化能力。
请看下列案例:
-
1、定义一个类,实现Serializable
class Demo implements Serializable { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Demo{" + "name='" + name + ''' + ", age=" + age + '}'; } } -
2、编写测试代码,对象实例化后将其写入文件中
package serializable; import java.io.*; public class SerializableTest { public static void main(String[] args) throws IOException, ClassNotFoundException { //System.out.println(System.getProperty("user.dir")); String names = System.getProperty("user.dir") + File.separator+"test"+File.separator+"serializable"+File.separator+"testSerializable"; Demo demo = new Demo(); demo.setName("xiangjiao"); demo.setAge(24); System.out.println(demo.toString()); // 将对象从内存中写到磁盘的指定文件中 FileOutputStream fileOutputStream = new FileOutputStream(names); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(demo); objectOutputStream.close(); } }代码执行后,其文件位置如下图所示:
-
3、从指定文件中读取内容,并转化为对应的类
package serializable; import java.io.*; public class SerializableTest { public static void main(String[] args) throws IOException, ClassNotFoundException { //System.out.println(System.getProperty("user.dir")); String names = System.getProperty("user.dir") + File.separator+"test"+File.separator+"serializable"+File.separator+"testSerializable"; Demo demo = new Demo(); demo.setName("xiangjiao"); demo.setAge(24); System.out.println(demo.toString()); // 将对象从内存中写到磁盘的指定文件中 // FileOutputStream fileOutputStream = new FileOutputStream(names); // ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); // objectOutputStream.writeObject(demo); // objectOutputStream.close(); // 从磁盘中读取文件 FileInputStream fileInputStream = new FileInputStream(names); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); Demo demo1 = (Demo) objectInputStream.readObject(); objectInputStream.close(); System.out.println(demo1.toString()); System.out.println(demo == demo1); } }其运行结果如下所示:
【疑问:】这里的对象地址为啥不是一样的!
如何保证反序列化后对象地址一致因为这个对象数据是JVM从数据流中解析出来的,并非是直接从堆中获取!
如果是为了测试必须保证对象地址一致,需要将序列化的子类以单例的形式进行测试。修改测试代码为单例模式,修改后的代码如下所示:
将对象信息序列化至磁盘文件中:
package serializable;
import java.io.*;
public class SerializableTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//System.out.println(System.getProperty("user.dir"));
String names = System.getProperty("user.dir") + File.separator+"test"+File.separator+"serializable"+File.separator+"testSerializable";
Demo instance = Demo.getInstance();
System.out.println(instance);
// 将对象从内存中写到磁盘的指定文件中
FileOutputStream fileOutputStream = new FileOutputStream(names);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(instance);
objectOutputStream.close();
}
}
class Demo implements Serializable {
private String name;
private int age;
private Demo (){}
private Demo(String name, int age) {
this.name = name;
this.age = age;
}
// 单例实例
private static Demo demo = new Demo("xiangjiao",24);
// 提供对外引用
public static Demo getInstance(){
return demo;
}
@Override
public String toString() {
return "Demo{" +
"name='" + name + ''' +
", age=" + age +
'}';
}
}
当对象从内存序列化至磁盘文件后,再执行下列的反序列化操作:
package serializable;
import java.io.*;
public class SerializableTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//System.out.println(System.getProperty("user.dir"));
String names = System.getProperty("user.dir") + File.separator+"test"+File.separator+"serializable"+File.separator+"testSerializable";
Demo instance = Demo.getInstance();
System.out.println(instance);
// 将对象从内存中写到磁盘的指定文件中
// FileOutputStream fileOutputStream = new FileOutputStream(names);
// ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
// objectOutputStream.writeObject(instance);
// objectOutputStream.close();
// 从磁盘中读取文件
FileInputStream fileInputStream = new FileInputStream(names);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
Demo demo1 = (Demo) objectInputStream.readObject();
objectInputStream.close();
System.out.println(demo1);
System.out.println(instance == demo1);
}
}
class Demo implements Serializable {
private String name;
private int age;
private Demo (){}
private Demo(String name, int age) {
this.name = name;
this.age = age;
}
// 单例实例
private static Demo demo = new Demo("xiangjiao",24);
// 提供对外引用
public static Demo getInstance(){
return demo;
}
@Override
public String toString() {
return "Demo{" +
"name='" + name + ''' +
", age=" + age +
'}';
}
}
此时控制台中运行的信息如下所示:
从运行结果中可以看出,当前两个对象的内存地址信息并不匹配。
当前是单例模型,如果内存地址不匹配,那也就说明采取序列化和反序列化操作,破坏了单例结构。
【思考:】如何才能解决这种问题呢,让其两个地址内存一致?
在java.io.Serializable的注释内容中,就已经对其进行了解释和说明。
那么我们就按照他说的来做,怎么使用呢?
在指定的类中,增加一个处理方法。
Object readResolve() throws ObjectStreamException{
return demo;
}
再次执行代码:
package serializable;
import java.io.*;
public class SerializableTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//System.out.println(System.getProperty("user.dir"));
String names = System.getProperty("user.dir") + File.separator+"test"+File.separator+"serializable"+File.separator+"testSerializable";
Demo instance = Demo.getInstance();
System.out.println(instance);
// 将对象从内存中写到磁盘的指定文件中
// FileOutputStream fileOutputStream = new FileOutputStream(names);
// ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
// objectOutputStream.writeObject(instance);
// objectOutputStream.close();
// 从磁盘中读取文件
FileInputStream fileInputStream = new FileInputStream(names);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
Demo demo1 = (Demo) objectInputStream.readObject();
objectInputStream.close();
System.out.println(demo1);
System.out.println(instance == demo1);
}
}
class Demo implements Serializable {
private String name;
private int age;
private Demo (){}
private Demo(String name, int age) {
this.name = name;
this.age = age;
}
// 单例实例
private static Demo demo = new Demo("xiangjiao",24);
// 提供对外引用
public static Demo getInstance(){
return demo;
}
@Override
public String toString() {
return "Demo{" +
"name='" + name + ''' +
", age=" + age +
'}';
}
Object readResolve() throws ObjectStreamException{
return demo;
}
}
运行后发现控制台出现以下报错信息:
出现这种问题的根源在于,文件中的序列化信息版本和现在已经更新过的版本不匹配。
在当前的序列化子类中,强制指定其版本号。如下所示:
private static final long serialVersionUID = -5461108964440966122L;
这里是这个数据,那是因为控制台中说明的。
重新生成对应文件,进行指定Java类的序列化和反序列化操作:
package serializable;
import java.io.*;
import java.util.concurrent.TimeUnit;
public class SerializableTest {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
//System.out.println(System.getProperty("user.dir"));
String names = System.getProperty("user.dir") + File.separator+"test"+File.separator+"serializable"+File.separator+"testSerializable";
Demo instance = Demo.getInstance();
System.out.println(instance);
// 将对象从内存中写到磁盘的指定文件中
FileOutputStream fileOutputStream = new FileOutputStream(names);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(instance);
objectOutputStream.close();
TimeUnit.SECONDS.sleep(2);
// 从磁盘中读取文件
FileInputStream fileInputStream = new FileInputStream(names);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
Demo demo1 = (Demo) objectInputStream.readObject();
objectInputStream.close();
System.out.println(demo1);
System.out.println(instance == demo1);
}
}
class Demo implements Serializable {
private static final long serialVersionUID = -5461108964440966122L;
private String name;
private int age;
private Demo (){}
private Demo(String name, int age) {
this.name = name;
this.age = age;
}
// 单例实例
private static Demo demo = new Demo("xiangjiao",24);
// 提供对外引用
public static Demo getInstance(){
return demo;
}
// @Override
// public String toString() {
// return "Demo{" +
// "name='" + name + ''' +
// ", age=" + age +
// '}';
// }
Object readResolve() throws ObjectStreamException{
return demo;
}
}
运行后,此时的控制台中可以看到地址信息是匹配的,如下所示:



