1.定义
Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。
将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化,也就是说,对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。
整个过程都是 Java 虚拟机(JVM)独立的,也就是说,在一个平台上序列化的对象可以在另一个完全不同的平台上反序列化该对象。
2.序列化前提
1.目标对象必须实现java.io.Serializable
接口
2.该类的所有属性必须是可序列化的。如果有一个属性不是可序列化的,则该属性必须注明是短暂(即 transient
)的。
3.演示
3.1 准备目标类
public class Employee implements java.io.Serializable
{
public String name;
public String address;
public transient int SSN; //不需序列化的字段
public int number;
public void mailCheck()
{
System.out.println("Mailing a check to " + name
+ " " + address);
}
}
3.2 序列化
import java.io.*;
public class SerializeDemo
{
public static void main(String [] args)
{
Employee e = new Employee();
e.name = "Reyan Ali";
e.address = "Phokka Kuan, Ambehta Peer";
e.SSN = 11122333;
e.number = 101;
try
{
FileOutputStream fileOut =
new FileOutputStream("/tmp/employee.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(e);
out.close();
fileOut.close();
System.out.printf("Serialized data is saved in /tmp/employee.ser");
}catch(IOException i)
{
i.printStackTrace();
}
}
}
3.3 反序列化
import java.io.*;
public class DeserializeDemo
{
public static void main(String [] args)
{
Employee e = null;
try
{
FileInputStream fileIn = new FileInputStream("/tmp/employee.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
e = (Employee) in.readObject();
in.close();
fileIn.close();
}catch(IOException i)
{
i.printStackTrace();
return;
}catch(ClassNotFoundException c)
{
System.out.println("Employee class not found");
c.printStackTrace();
return;
}
System.out.println("Deserialized Employee...");
System.out.println("Name: " + e.name);
System.out.println("Address: " + e.address);
System.out.println("SSN: " + e.SSN);
System.out.println("Number: " + e.number);
}
}
3.4使用Externalizable实现序列化
用Externalizable接口实现序列化时需要注意两点:
必须要提供公有的无参构造函数,否则会报InvalidClassException。
必须要在writeExternal和readExternal中自己去实现序列化过程。
public class People implements Externalizable {
private String name;
private int age;
public People() {
}
public People(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(name);
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = (String) in.readObject();
age = in.readInt();
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
People people = new People("bobo", 26);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(people);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
People serialPeople = (People) objectInputStream.readObject();
System.out.println("name:" + serialPeople.getName() + " age:" + serialPeople.getAge());
}
}
4.注意点
1.readObject() 方法中的 try/catch代码块尝试捕获 ClassNotFoundException 异常。对于 JVM 可以反序列化对象,它必须是能够找到字节码的类。如果JVM在反序列化对象的过程中找不到该类,则抛出一个 ClassNotFoundException 异常。
2.serialVersionUID能加的话尽量加上,serialVersionUID 是根据该类名、方法名等数据生产的一个整数,用来验证版本是否一致。如果不加这个字段,当你的类修改了字段,在反序列化的时候会直接报异常:InvalidCastException,导致无法完成反序列化。
5.序列化的应用场景
1.可以用来深度复制对象,效率比json复制要略高
2.把的内存中的对象保存到一个文件中或者数据库
3.用序列化在网络上传送对象,但现在基本都用json,很少有谁会直接传输序列化对象流,你很难要求别人用的是java或者拥有一个和你一模一样的类,除非你在搞C/S架构,想来这种情况应用场景更多
4.过RMI传输对象
6.序列化框架
6.1JDKSerialize
1.无法跨语言。这应该是java序列化最致命的问题了。由于java序列化是java内部私有的协议,其他语言不支持,导致别的语言无法反序列化,这严重阻碍了它的应用。
2.序列后的码流太大。java序列化的大小是二进制编码的5倍多!
3.序列化性能太低。java序列化的性能只有二进制编码的6.17倍,可见java序列化性能实在太差了。
6.2 FastjsonSerialize
一个JSON库涉及的最基本功能就是序列化和反序列化。Fastjson支持java bean的直接序列化。 使用com.alibaba.fastjson.JSON这个类进行序列化和反序列化。
6.3 FSTSerialize
FST fast-serialization 是重新实现的 Java 快速对象序列化的开发包。序列化速度更快(2-10倍)、体积更小,而且兼容 JDK 原生的序列化。
要求 JDK 1.7 支持。
6.4 google的Protobuf
1.结构化数据存储格式(xml,json等)
2.高性能编解码技术
3.语言和平台无关,扩展性好
4.支持java,C++,Python三种语言。
6.5 faceBook的Thrift
1.Thrift支持多种语言(C++,C#,Cocoa,Erlag,Haskell,java,Ocami,Perl,PHP,Python,Ruby,和SmallTalk)
2.Thrift适用了组建大型数据交换及存储工具,对于大型系统中的内部数据传输,相对于Json和xml在性能上和传输大小上都有明显的优势。
3.Thrift支持三种比较典型的编码方式。(通用二进制编码,压缩二进制编码,优化的可选字段压缩编解码)
6.6 Kryo
Kryo的运行速度是java Serializable 的20倍左右 ,Kryo的文件大小是java Serializable的一半左右
6.7 Hessian
1.默认支持跨语言
2.较慢