简单来说序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化,流的概念这里不用多说(就是I/O),我们可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间(注:要想将对象传输于网络必须进行流化)!在对对象流进行读写操作时会引发一些问题,而序列化机制正是用来解决这些问题的!
恰当的序列化协议不仅可以提高系统的通用性、强壮型、安全性、优化性能。同时还能让系统更加易于调试和扩展。
Java语言本身提供了实现对象的序列化的方式,需要通过实现Serializable接口。然后采用ObjectOutPutStream以及ObjectInputStream进行对象的序列化、反序列化。在序列化时还需要添加serialVersuibUID。
package com.fish.bone;
import java.io.Serializable;
/**
* @program: JavaStudyTest
* @Date: 2019/3/19 0019 上午 8:53
* @Author: <.*)#)))<
* @Description:
*/
public class Fish implements Serializable {
private static final long serialVersionUID = 7176862405761602267L;
private String liveIn;
private int length;
private int weight;
public String getLiveIn() {
return liveIn;
}
public void setLiveIn(String liveIn) {
this.liveIn = liveIn;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
@Override
public String toString() {
return "Fish{" +
"liveIn='" + liveIn + '\'' +
", length=" + length +
", weight=" + weight +
'}';
}
}
package com.fish.bone;
import java.io.*;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args ) throws IOException, ClassNotFoundException {
SerializeObject();
DeSerializeObject();
}
private static void SerializeObject() throws IOException {
Fish fish = new Fish();
fish.setLength(2);
fish.setLiveIn("大海");
fish.setWeight(30);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File("Fish")));
objectOutputStream.writeObject(fish);
objectOutputStream.flush();
System.out.println("序列化完成");
}
private static void DeSerializeObject() throws IOException, ClassNotFoundException {
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("Fish"));
Fish fish = (Fish) inputStream.readObject();
System.out.println(fish);
System.out.println("反序列化完成");
}
}
简单来说,Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。(例如:在序列化时加入serialVersionUID,进行反序列化时注销serialVersionUID,就会出现反序列化失败)
Exception in thread "main" java.io.InvalidClassException: com.fish.bone.Fish; local class incompatible: stream classdesc serialVersionUID = 7176862405761602267, local class serialVersionUID = 8189052949692937727
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1885)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1751)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
at com.fish.bone.App.DeSerializeObject(App.java:39)
at com.fish.bone.App.main(App.java:14)
在类中存在静态变量,并对该类进行序列化时是不会保存静态变量状态的。简单来说类中的static静态变量是不能被序列化的,反序列化后类中static静态变量的值为当前JVM中对应static变量的值,这个值是JVM中的不是反序列化得出的。
package com.fish.bone;
import java.io.Serializable;
/**
* @program: JavaStudyTest
* @Date: 2019/3/19 0019 上午 8:53
* @Author: <.*)#)))<
* @Description:
*/
public class Fish implements Serializable {
private static final long serialVersionUID = 7176862405761602267L;
private String liveIn;
public static int length =2;
private int weight;
public String getLiveIn() {
return liveIn;
}
public void setLiveIn(String liveIn) {
this.liveIn = liveIn;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
@Override
public String toString() {
return "Fish{" +
"liveIn='" + liveIn + '\'' +
", length=" + length +
", weight=" + weight +
'}';
}
}
package com.fish.bone;
import java.io.*;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args ) throws IOException, ClassNotFoundException {
SerializeObject();
Fish.length = 5;
DeSerializeObject();
}
private static void SerializeObject() throws IOException {
Fish fish = new Fish();
//fish.setLength(2);
fish.setLiveIn("大海");
fish.setWeight(30);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File("Fish")));
objectOutputStream.writeObject(fish);
objectOutputStream.flush();
System.out.println("序列化完成");
fish.setLiveIn("河流");
objectOutputStream.writeObject(fish);
objectOutputStream.flush();
objectOutputStream.close();
System.out.println("序列化修改完成");
}
private static void DeSerializeObject() throws IOException, ClassNotFoundException {
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("Fish"));
Fish fish = (Fish) inputStream.readObject();
System.out.println(fish);
//Fish fish2 = (Fish) inputStream.readObject();
System.out.println(fish.length);
System.out.println("反序列化完成");
}
}
输出结果:
序列化完成
序列化修改完成
Fish{liveIn='大海', length=5, weight=30}
5
反序列化完成
Process finished with exit code 0
如上所示,如果静态变量参与序列化那么输出结果应该是序列化时的值2,而不是序列化之后才修改为的5。
此处有一个问题:
在我对Fish序列化之后,尝试在原对象修改LiveIn字段的值重新进行序列化。在反序列化后读取到的值还是之前的。这个原因暂时还不清楚。如果重新new对象读取则没有问题。
public Object deepClone() throws IOException, ClassNotFoundException {
//序列化
ByteArrayOutputStream baos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(baos);
oos.writeObject(this);
//反序列化
ByteArrayInputStream bais=new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois=new ObjectInputStream(bais);
return ois.readObject();
}
由于Java本身序列化的问题,以至于在后来很长一段时间内,基于XML格式编码的对象序列化机制成为了主流,一方面解决了多语言兼容问题,另一方面比二进制的序列化方式更容易理解。以至于基于XML的SOAP协议及对应的WebService框架在很长一段时间内成为各个主流开发语言的必备的技术。
再到后来,基于JSON的简单文本格式编码的HTTP REST接口又基本上取代了复杂的Web Service接口,成为分布式架构中远程通信的首要选择。但是JSON序列化存储占用的空间大、性能低等问题,同时移动客户端应用需要更高效的传输数据来提升用户体验。在这种情况下与语言无关并且高效的二进制编码协议就成为了大家追求的热点技术之一。首先诞生的一个开源的二进制序列化框架-MessagePack。它比google的Protocol Buffers出现得还要早。
FastJson/Gson/JackSon序列化和反序列化性能对比
XML的优点:
A.格式统一,符合标准;
B.容易与其他系统进行远程交互,数据共享比较方便。
XML的缺点:
A.XML文件庞大,文件格式复杂,传输占带宽;
B.服务器端和客户端都需要花费大量代码来解析XML,导致服务器端和客户端代码变得异常复杂且不易维护;
C.客户端不同浏览器之间解析XML的方式不一致,需要重复编写很多代码;
D.服务器端和客户端解析XML花费较多的资源和时间。