Java对象的序列化和反序列化
1. 概述
序列化:将Java对象转换为二进制数据流的过程
反序列化:将序列化过程生成的二进制字节流转化为Java对象的过程
主要目的:
- 通过网络传输Java对象
- 将Java对象保存在本地文件系统,数据库。
应用场景:
- 远程RPC调用
- 将对象存储到Redis缓存数据库
2. 具体实现
2.1 JDK 方式
ObjectOutputStream:内存中的对象—>存储中的文件、通过网络传输出去:序列化过程
ObjectInputStream:存储中的文件、通过网络接收过来 —>内存中的对象:反序列化过程
2.1.1 序列化步骤
-
创建类,实现接口:Serializable
-
当前类提供一个全局常量:serialVersionUID
序列化号 serialVersionUID 属于版本控制的作用。序列化的时候 serialVersionUID 也会被写入二级制序列,当反序列化时会检查 serialVersionUID 是否和当前类的 serialVersionUID 一致
-
使用对象流ObjectOutputStream进行序列化
PS:
- 序列化的对象内部如果有其他对象的引用,该对象也必须可序列化(实现Serializable,全局常量:serialVersionUID)
- 不能序列化static和transient修饰的成员变量
2.1.2 序列化代码
//1. 创建可序列化对象
public class Person implements Serializable {//实现接口,该接口是一个声明式接口
//2. 声明全局变量
private final static long serialVersionUID = 34L;//该数可以是任意数
public String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class test {
public static void main(String[] args) {
ObjectOutputStream oos = null;
try {
//3. 通过对象流序列化对象
//3.1 创建文件输入流对象传入构造器,指定对象序列化数据存储位置
oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
oos.writeObject(new Person("jim",23));
oos.flush();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
//不要忘记关闭资源哦!!!
oos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
反序列化方式不再赘述,直接贴代码!!!
public class test {
public static void main(String[] args) {
ObjectInputStream ois = null;
try {
//反序列化
ois = new ObjectInputStream(new FileInputStream("object.dat"));
Person person = (Person) ois.readObject();
System.out.println(person.name);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}finally {
try {
ois.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
But!!!学完这些东西,才发现发现~~~基本不用这种方式!
因为:
- 不支持跨语言调用
- 性能差:序列化之后的字节文件体积较大,要知道我们的网络资源有时候是很宝贵的,所以传输成本越低越好。
那么:有一些序列化框架十分优秀,可以帮助我们完成序列化和反序列化的操作
2.2 序列化框架
Kryo
首先,先贴上Kryo的GitHub地址kryo,学技术当然第一时间要看官方资料啦。具体的信息可以自行前往项目地址查看这里不再赘述。
Kryo 是一个高性能的序列化/反序列化工具,由于其变长存储特性并使用了字节码生成机制,拥有较高的运行速度和较小的字节码体积。
另外,Kryo 已经是一种非常成熟的序列化实现了,已经在 Twitter、Groupon、Yahoo 以及多个著名开源项目(如 Hive、Storm)中广泛的使用。
下面,对kryo进行简单使用。
(1)导入Kryo包
下面是Maven依赖,不使用maven和从源代码构建的方式请参考官网
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>5.3.0</version>
</dependency>
(2)快速使用
public class KryoTest {
static public void main (String[] args) throws Exception {
Kryo kryo = new Kryo();
Person person = new Person("jim",23);
//Kryo 为了提供性能和减小序列化结果体积,提供注册的序列化对象类的方式。在注册时,会为该序列化类生成 int ID,后续在序列化时使用 int ID 唯一标识该类型。注册的方式如下:
kryo.register(person.class);
person object = new person();
object.value = "Hello Kryo!";
Output output = new Output(new FileOutputStream("file.bin"));
kryo.writeObject(output, object);
output.close();
Input input = new Input(new FileInputStream("file.bin"));
person object2 = kryo.readObject(input, person.class);
System.out.println(person.name);
input.close();
}
static public class person {
String value;
}
}
除了Kryo以外还有许多序列化框架:
- FST:https://github.com/RuedigerMoeller/fast-serialization
- Protobuf:https://github.com/protocolbuffers/protobuf
- protoStuff:https://github.com/protostuff/protostuff