序列化不仅在java面试中是个较为常见的面试题,而且在实际项目开发中也是经常打交道的,今天咱们就来聊一聊关于序列化与反序列化那些事儿。
什么是序列化和反序列化?
序列化:把对象转换为字节序列的过程。
反序列化:把字节序列恢复为对象的过程。
序列化的作用
(1) 把对象的字节序列永久地保存到硬盘上(通常存放在一个文件中);
(2) 在网络上传送对象的字节序列。
java代码实现序列化
(1)使用序列化与不使用的区别
现在准备把一个对象中的数据写入到硬盘,并读取之前往硬盘写入的数据。
建立一个猫的实体类
/**
* @Description: 猫的实体类
* @CreateDate: 2019/6/24 10:42
*/
public class Cat {
private String name;
private Integer age;
private String color;
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 getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + ''' +
", age=" + age +
", color='" + color + ''' +
'}';
}
}
测试类
import java.io.*;
/**
* 序列化测试
*/
public class DemoSerializable04 {
public static void main(String args[]) throws IOException, ClassNotFoundException {
serialization();
deserialization();
}
//序列化
public static void serialization() throws IOException {
Cat cat = new Cat();
cat.setName("Tom");
cat.setAge(3);
cat.setColor("black");
//指定位置到输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("D:temporary/cat.txt")));
//把对象序列化到输出流中
oos.writeObject(cat);
System.out.println("对象序列化成功!");
//关闭Io流
oos.close();
}
//反序列化
public static void deserialization() throws IOException, ClassNotFoundException {
//指定位置到输入流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("D:temporary/cat.txt")));
//读取输入流中的对象
Cat cat = (Cat) ois.readObject();
System.out.println("对象反序列化成功!");
System.out.println(cat.toString());
}
}
执行后,会发现抛异常,提示无法往硬盘写入数据。
D:bprogramSoftwarejdkjdk1.8.0_131binjava ...
Exception in thread "main" java.io.NotSerializableException: Cat
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at DemoSerializable04.serialization(DemoSerializable04.java:23)
at DemoSerializable04.main(DemoSerializable04.java:8)
这时候在对Cat对象实现Serializable,再次运行,OK,可以正常写并读数据。
D:bprogramSoftwarejdkjdk1.8.0_131binjava "...
对象序列化成功!
对象反序列化成功!
Cat{name='Tom', age=3, color='black'}
(2)serialVersionUID有何作用
现在Cat类中添加一个type的属性,并只执行反序列化,这时候程序就会抛出异常,标明实体中的uid与反序列化时生成的uid不同。
D:bprogramSoftwarejdkjdk1.8.0_131binjava "...
Exception in thread "main" java.io.InvalidClassException: Cat; local class incompatible: stream classdesc serialVersionUID = -1858351317844297891, local class serialVersionUID = 4327445649782673671
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1843)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1713)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2000)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1535)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)
at DemoSerializable04.deserialization(DemoSerializable04.java:35)
at DemoSerializable04.main(DemoSerializable04.java:9)
解决uid不同的问题的答案,就是给实体类中给serialVersionUID 赋值(private static final long serialVersionUID = 1L),这样就可以
保证uid不会有冲突了。
(3)static 与transient关键字对序列化的影响
现在往Cat实体类中的age属性前加上static进行修饰,接着运行测试类。卧槽,这和书上说的不一样啊,static修饰的属性不能被序列
化啊,但其实static的静态成员变量是不能被序列化这个观点是正确的,造成可以拿到static序列化之后的假象是因为static 属于类的,得到
的是JVM已经加载好的Class的static变量的值。
D:bprogramSoftwarejdkjdk1.8.0_131binjava ...
对象序列化成功!
对象反序列化成功!
Cat{name='Tom', age=6, color='black', type='null'}
单独执行读取代码后,会发现age值为null,可以证明static的静态成员变量并不能被序列化。
D:bprogramSoftwarejdkjdk1.8.0_131binjava ...
对象反序列化成功
Cat{name='Tom', age=null, color='black', type='null'}
同理,在Cat类中color属性前添加transient进行修饰,然后单独执行读取代码后,会发现color值为null,transient修饰的属性不能被序列化。
D:bprogramSoftwarejdkjdk1.8.0_131binjava ...
对象反序列化成功!
Cat{name='Tom', age=null, color='null', type='null'}
序列化的方式(性能由低至高)
Java Serialization(主要是采用JDK自带的Java序列化实现,性能很不理想)
Json(目前有两种实现,一种是采用的阿里的fastjson库,另一种是采用dubbo中自己实现的简单json库)
FastJson(阿里的fastjson库)
Hession(它基于HTTP协议传输,使用Hessian二进制序列化,对于数据包比较大的情况比较友好。)
Dubbo Serialization(阿里dubbo序列化)
FST(高性能、序列化速度大概是JDK的4-10倍,大小是JDK大小的1/3左右)
Kryo
总结
序列化主要是为了跨平台,实现对象的一致性,可在不同的平台上,保持自己原有的属性和方法不变。