1、什么是序列化
引用百度百科的定义:序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。
与之对应的就是反序列化:将存储的或网络传输来的数据转为真实对象信息的过程。
2、为什么要序列化
了解计算机的都知道,在计算机和网络上只认识流的信息,即0/1,这就说明了我们直接定义的一个对象是没法在网络上直接传输的,这就需要一种手段将我们的对象转为流的状态,即序列化。
3、谁来进行序列化
对象由谁来使用、传输,则谁就需要进行序列化,从程序角度来看,对象就是由我们的程序来进行序列化,更直接一点就是写程序的人来序列化。
4、什么时候进行序列化
合适时机的序列化可以提高我们的资源利用,若一个对象下一步就需要传输或存储了,这个时候我们认为就需要序列化了,若过早的进行序列化,则在内存中白白的占用空间。
5、有哪些序列化方式
序列化方式是多样的,我们需要根据项目的实际情况来做出选择,在序列化过程中,从时间和空间上找到一种平衡。作为Java开发者,我们常见的序列化方式有:JDK自带的序列化方式、JSON、ProtoBuf、Hessian、Thrift、XML等。
6、如何进行序列化
这里用代码进行了一些测试,大家自己实践后做出选择。
package com.hz.cloud;
import java.io.Serializable;
/**
* JDK自带的序列化
* @author Dong
* @version 1.0
* @date 2022/3/30
*/
public class Person implements Serializable {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package com.hz.cloud;
/**
* @author Dong
* @version 1.0
* @date 2022/3/30
*/
public class PersonInit {
public static Person initPerson() {
Person person = new Person();
person.setName("公子奇");
person.setAge(18);
return person;
}
}
6.1 JDK自带
package com.hz.cloud;
import java.io.*;
/**
* @author Dong
* @version 1.0
* @date 2022/3/30
*/
public class JDKSerializable {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Person person = PersonInit.initPerson();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//序列化
ObjectOutputStream oos = new ObjectOutputStream(baos);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
oos.writeObject(person);
oos.flush();
}
System.out.println("JDK自带序列化时间:" + (System.currentTimeMillis() - startTime) + " ms, " + "占用空间: " + baos.toByteArray().length);
/*
* 1 :JDK自带序列化时间:8 ms, 占用空间: 90
* 10:JDK自带序列化时间:11 ms, 占用空间: 135
* 100:JDK自带序列化时间:9 ms, 占用空间: 585
* 1000:JDK自带序列化时间:11 ms, 占用空间: 5085
* 10000:JDK自带序列化时间:10 ms, 占用空间: 50085
*/
//反序列化
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
Person p = (Person) ois.readObject();
System.out.println(p);
}
}
JDK由于自身的局限性,我们一般不做选择。数据传输出去后,本身就是为了被别人使用,而JDK自带的序列化方式不支持跨语言,还有就是空间占用上远远高于数据本身。
6.2 JSON
JSON在当前分布式架构流行的情况下使用最广,越来越多的配置文件也开始用JSON了。
6.2.1 FastJson
package com.hz.cloud;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
/**
* @author Dong
* @version 1.0
* @date 2022/3/30
*/
public class FastJsonSerializable {
public static void main(String[] args) {
Person person = PersonInit.initPerson();
// 序列化
String json = null;
long startTime = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
json = JSON.toJSONString(person);
}
System.out.println("FastJson序列化时间:" + (System.currentTimeMillis() - startTime) + " ms, " + "占用空间: " + json.getBytes().length);
// 1: FastJson序列化时间:81 ms, 占用空间: 29
// 10: FastJson序列化时间:80 ms, 占用空间: 29
// 100: FastJson序列化时间:77 ms, 占用空间: 29
// 1000: FastJson序列化时间:79 ms, 占用空间: 29
// 10000: FastJson序列化时间:96 ms, 占用空间: 29
// 100000: FastJson序列化时间:139 ms, 占用空间: 29
// 1000000: FastJson序列化时间:229 ms, 占用空间: 29
// 10000000: FastJson序列化时间:1240 ms, 占用空间: 29
//反序列化
Person p = JSON.parseObject(json, Person.class);
System.out.println(p);
}
}
6.2.2 Jackson
package com.hz.cloud;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ObjectWriter;
import java.io.IOException;
/**
* @author Dong
* @version 1.0
* @date 2022/3/30
*/
public class JacksonSerializable {
public static void main(String[] args) throws IOException {
Person person = PersonInit.initPerson();
ObjectMapper mapper = new ObjectMapper();
ObjectWriter writer = mapper.writerWithDefaultPrettyPrinter();
// 序列化
String json = null;
long startTime = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
json = writer.writeValueAsString(person);
}
System.out.println("Jackson序列化时间:" + (System.currentTimeMillis() - startTime) + " ms, " + "占用空间: " + json.getBytes().length);
// 1: Jackson序列化时间:22 ms, 占用空间: 43
// 10: Jackson序列化时间:22 ms, 占用空间: 43
// 100: Jackson序列化时间:27 ms, 占用空间: 43
// 1000: Jackson序列化时间:33 ms, 占用空间: 43
// 10000: Jackson序列化时间:50 ms, 占用空间: 43
// 100000: Jackson序列化时间:110 ms, 占用空间: 43
// 1000000: Jackson序列化时间:286 ms, 占用空间: 43
// 10000000: Jackson序列化时间:1884 ms, 占用空间: 43
//反序列化
Person p = mapper.reader(Person.class).readValue(json);
System.out.println(p);
}
}
6.2.3 Gson
package com.hz.cloud;
import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
/**
* @author Dong
* @version 1.0
* @date 2022/3/30
*/
public class GsonSerializable {
public static void main(String[] args) {
Person person = PersonInit.initPerson();
Gson gson = new Gson();
// 序列化
String json = null;
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1; i++) {
json = gson.toJson(person);
}
System.out.println("Gson序列化时间:" + (System.currentTimeMillis() - startTime) + " ms, " + "占用空间: " + json.getBytes().length);
// 1: Gson序列化时间:14 ms, 占用空间: 29
// 10: Gson序列化时间:15 ms, 占用空间: 29
// 100: Gson序列化时间:15 ms, 占用空间: 29
// 1000: Gson序列化时间:23 ms, 占用空间: 29
// 10000: Gson序列化时间:41 ms, 占用空间: 29
// 100000: Gson序列化时间:138 ms, 占用空间: 29
// 1000000: Gson序列化时间:532 ms, 占用空间: 29
// 10000000: Gson序列化时间:4087 ms, 占用空间: 29
//反序列化
Person p = gson.fromJson(json, Person.class);
System.out.println(p);
}
}
6.3 Hessian
package com.hz.cloud;
import com.baidu.bjf.remoting.protobuf.Codec;
import com.baidu.bjf.remoting.protobuf.ProtobufProxy;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
* @author Dong
* @version 1.0
* @date 2022/3/30
*/
public class HessianSerializable {
public static void main(String[] args) throws IOException {
Person person = PersonInit.initPerson();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Hessian2Output out = new Hessian2Output(baos);
// 序列化
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1; i++) {
out.writeObject(person);
}
out.close();
System.out.println("Hessian序列化时间:" + (System.currentTimeMillis() - startTime) + " ms, " + "占用空间: " + baos.toByteArray().length);
// 1: Hessian序列化时间:24 ms, 占用空间: 43
// 10: Hessian序列化时间:27 ms, 占用空间: 61
// 100: Hessian序列化时间:29 ms, 占用空间: 241
// 1000: Hessian序列化时间:29 ms, 占用空间: 2041
// 10000: Hessian序列化时间:31 ms, 占用空间: 16352
// 100000: Hessian序列化时间:42 ms, 占用空间: 196224
// 1000000: Hessian序列化时间:65 ms, 占用空间: 1994944
// 10000000: Hessian序列化时间:368 ms, 占用空间: 19998496
//反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
Hessian2Input in = new Hessian2Input(bais);
Person p = (Person) in.readObject();
System.out.println(p);
}
}
6.4 ProtoBuf
我们首先需要对上面的 Person
类进行修改:
package com.hz.cloud;
import com.baidu.bjf.remoting.protobuf.FieldType;
import com.baidu.bjf.remoting.protobuf.annotation.Protobuf;
import java.io.Serializable;
/**
* JDK自带的序列化
* @author Dong
* @version 1.0
* @date 2022/3/30
*/
public class Person implements Serializable {
@Protobuf(fieldType = FieldType.STRING)
private String name;
@Protobuf(fieldType = FieldType.INT32)
private int age;
...
}
package com.hz.cloud;
import com.alibaba.fastjson.JSON;
import com.baidu.bjf.remoting.protobuf.Codec;
import com.baidu.bjf.remoting.protobuf.ProtobufProxy;
import com.baidu.bjf.remoting.protobuf.utils.ProtobufProxyUtils;
import com.baidu.jprotobuf.com.squareup.protoparser.ProtoParser;
import java.io.IOException;
/**
* @author Dong
* @version 1.0
* @date 2022/3/30
*/
public class ProtobufSerializable {
public static void main(String[] args) throws IOException {
Person person = PersonInit.initPerson();
Codec<Person> codec = ProtobufProxy.create(Person.class, false);
// 序列化
byte[] bytes = null;
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1; i++) {
bytes = codec.encode(person);
}
System.out.println("Protobuf序列化时间:" + (System.currentTimeMillis() - startTime) + " ms, " + "占用空间: " + bytes.length);
// 1: Protobuf序列化时间:7 ms, 占用空间: 13
// 10: Protobuf序列化时间:8 ms, 占用空间: 13
// 100: Protobuf序列化时间:6 ms, 占用空间: 13
// 1000: Protobuf序列化时间:10 ms, 占用空间: 13
// 10000: Protobuf序列化时间:26 ms, 占用空间: 13
// 100000: Protobuf序列化时间:90 ms, 占用空间: 13
// 1000000: Protobuf序列化时间:778 ms, 占用空间: 13
// 10000000: Protobuf序列化时间:5470 ms, 占用空间: 13
//反序列化
Person p = codec.decode(bytes);
System.out.println(p);
}
}
7、总结
从上面我们可以看到,Google 的 ProtoBuf 占用空间最小,小的占用在同样的网络环境下,传输必然也更快,ProtoBuf 使用的也越来越多。Json框架最多,使用的最广,不过各个框架的侧重点不一样,我们根据项目情况来做出选择。