序列化,反序列化
一、什么是序列化?
把 JAVA 对象
转换成可保存可传输的形式的过程就是序列化。
二、什么是反序列化?
把一个对象序列化的结果恢复成原对象的过程就是反序列化。
序列化,反序列化的实现
一、Java 中如何实现序列化
一个类只需要实现 Serializable 接口就可以使其实例对象可序列化和反序列化。示例如下:
//这是一个Person类
import java.io.Serializable;
/**
* Created with IntelliJ IDEA.
* Author:rzx * Date:2017/10/24
*/
public class Person implements Serializable {
private String name;
private Integer age;
private String desc;
public Person(String name, Integer age,String desc) {
this.name = name;
this.age = age;
this.desc = desc;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return this.getClass().getSimpleName()+"["+this.getName()+","+this.getAge()+","+this.desc+"]";
}
}
下面我们来手动序列化/反序列化一个 Person 的实例
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* Created with IntelliJ IDEA.
* Author:rzx * Date:2017/10/24
*/
public class SerialiableTest {
public static void main(String[] args) throws Exception{
//实例化一个对像
Person person = new Person("Aami",18,"this is a Person Object");
//看看对象内容
System.out.println(person.toString());// Person[Aami,18,this is a Person Object]
//序列化:的大了一个二进制文件,这样我们就可以在网络中传输了这个对象了
ObjectOutputStream osu = new ObjectOutputStream(new FileOutputStream("Person.sl"));
osu.writeObject(person);
//反序列化:得到一个序列化的文件,我们把他恢复成一个对象
ObjectInputStream osi = new ObjectInputStream(new FileInputStream("Person.sl"));
Object obj = osi.readObject();
//我们将一个对象反序列化了,来看看它是谁
System.out.println(obj);
//结果是:Person[Aami,18,this is a Person Object]原来是一个Person对象。
//尽管我们用一个Object接受它,但它很清楚自己是一个Person
}
}
运行结果:
Person[Aami,18,this is a Person Object]
Person[Aami,18,this is a Person Object]
二、自动序列化是如何完成的
实例中显示调用 ObjectInputStream/ObjectOutputStream,来进行序列化和反序列化,在很多时候我们并没有(或者并不需要写)写这些代码,当我们实现了 Serializable
接口的时候就声明这是个可序列化的类,当需要序列化的时候(如:网络传输。。。)系统会自动将其序列化
。进行序列化、反序列化时,虚拟机会首先试图调用对象里的 writeObject 和 readObject 方法,进行用户自定义的序列化和反序列化。如果没有这样的方法,那么默认调用的是 ObjectOutputStream的defaultWriteObject以及ObjectInputStream的defaultReadObject方法
。换言之,利用自定义的 writeObject 方法和 readObject 方法,用户可以自己控制序列化和反序列化的过程。
transient 关键字
Transient 关键字:用于标识此属性无需序列化,那么在序列化的时候就不会对此属性进行序列化。示例如下:
//将Person中的desc属性加上transient修饰,其他代码不变:
private transient String desc;
再次运行结果:
Person[Aami,18,this is a Person Object]
Person[Aami,18,null]
我们发现反序列化之后的 desc 属性变成了 null,说明此属性并未序列化。
serialVersionUID:序列化版本号
我们将上一步的代码再做一点修改:Person 类加上一个属性 sex,此时 Person,有了四个属性。
private String sex;
我们 再运行SerialiableTest类的反序列化部分
即只运行如下部分:
//反序列化:得到一个序列化的文件,我们把他恢复成一个对象
ObjectInputStream osi = new ObjectInputStream(new FileInputStream("Person.sl"));
Object obj = osi.readObject();
//我们将一个对象反序列化了,来看看它是谁
System.out.println(obj);
这一步是什么意思呢?就是说我们把一个对象进行了序列化,得到一个文件 Person.s1,然后呢修改了这个 Person 类,然后呢我们去反序列化这个文件。得到结果如下:
Exception in thread "main"
java.io.InvalidClassException: com.rzx.train.serialiableT.Person;
local class incompatible:
stream classdesc serialVersionUID = 5957172176274511819,
local class serialVersionUID = 3320482973311753842
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1630)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1781)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
at com.rzx.train.serialiableT.SerialiableTest.main(SerialiableTest.java:26)
爆出了一个异常:本地类不匹配,并且 stream classdesc serialVersionUID = 5957172176274511819,而 local class serialVersionUID = 3320482973311753842,好像是说序列化的类的 serialVersionUID 和当前的类的 serialVersionUID 不一致导致他不知道要把自己反序列化程哪一个类了。。他找不到创造自己的类了。
**如何保证序列化后一定能够反序列化呢? **
我们手动给这个类指定一个 serialVersionUID 就可以了,尽管类的内容修改了,但是这个 serialVersionUID 没变,反序列化依然能找到这个类。
private static final long serialVersionUID = 1361127491317406689L;
**指定 serialVersionUID 方式: **
1、默认是:1L
2、根据类名、接口名、成员方法以及属性等来生成一个 64 位的 Hash 字段
在 IDE 中会自动提示我们添加一个 serialVersionUID。
...待续
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于