0%

序列化

前言:因为dubbo的问题,补一下Java序列化的基础知识

概念

序列化:将java对象转换为二进制并储存到磁盘

反序列化:将二进制数据转换为java对象

序列化的作用

  1. 想把内存中的对象保存到一个文件中或者数据库中时候;
  2. 想用套接字在网络上传送对象的时候;

序列化实现

实现Serializable接口。使用IO流中的对象流可以实现序列化操作,将对象保存到文件,再读取出来。

User

1
2
3
4
5
6
7
8
9
10
11
import java.io.Serializable;

/**
* @author shency
* @description:
*/
public class User implements Serializable {
private static final long serialVersionUID = -3361520124163938150L;
private String name;
……
}

SerializeUtil

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
* @author shency
* @description: 序列化工具类
* @date: 2019/10/24
*/
public class SerializeUtil {

public static void outputObject(Object object) {
String fileDstDir = "/tmp";
File dir = new File(fileDstDir);
if (!dir.exists()) {
dir.mkdirs();
}
fileDstDir += "/" + object.getClass().getName();
ObjectOutputStream objectOutputStream = null;
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(fileDstDir);
objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(object);
objectOutputStream.close();
fileOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}

public static Object inputObject(Class clazz) {
String fileDstDir = "/tmp";
File dir = new File(fileDstDir);
if (!dir.exists()) {
dir.mkdirs();
}
fileDstDir += "/" + clazz.getName();
ObjectInputStream objectInputStream = null;
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(fileDstDir);
objectInputStream = new ObjectInputStream(fileInputStream);
Object object = objectInputStream.readObject();
objectInputStream.close();
fileInputStream.close();
return object;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* @author shency
* @description: TODO
* @date: 2019/10/24
*/
public class Solution {
public static void main(String[] args) {
User user = new User();
user.setName("husky");
SerializeUtil.outputObject(user);
user = (User)SerializeUtil.inputObject(User.class);
if(user != null){
System.out.println(user.getName());
}

// user = (User)HessianSerializeUtil.deserialize(bytes);
// System.out.println(user.getName());
}
}

运行结果

生成二进制文件

image-20191226003407981

image-20191226003418109

将二进制文件转换为java对象

image-20191226003429659

如果类User没有实现Serializable接口,将对象输出就会报错

image-20191226003443424

serialVersionUID

serialVersionUID是类序列化时的一个标记,用于验证版本一致性,对象序列化操作的时候JVM会把当前类的serialVersionUID写入到序列化文件中,进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本机相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。

当实现java.io.Serializable接口的类没有显式地定义一个serialVersionUID变量时候,Java序列化机制会根据编译的Class自动生成一个serialVersionUID作序列化版本比较用。但是,Java官方强烈建议所有要序列化的类都显示地声明serialVersionUID字段,因为如果高度依赖于JVM默认生成serialVersionUID,可能会导致其与编译器的实现细节耦合,这样可能会导致在反序列化的过程中发生意外的InvalidClassException异常。

hessian介绍

dubbo使用hessian2序列化

hessia是n基于Hessian的远程调用协议。效率高于java自带的序列化

  • 连接个数:多连接
  • 连接方式:短连接
  • 传输协议:HTTP
  • 传输方式:同步传输
  • 序列化:表单序列化
  • 适用范围:传入传出参数数据包大小混合,提供者比消费者个数多,可用浏览器查看,可用表单或URL传入参数,暂不支持传文件。
  • 适用场景:需同时给应用程序和浏览器JS使用的服务。

hessian与java序列化的区别

Java序列化

Java序列化会把要序列化的对象类的元数据和业务数据全部序列化为字节流,而且是把整个继承关系上的东西全部序列化。它序列化出来的字节流是对那个对象结构到内容的完全描述,包含所有的信息,因此效率较低而且字节流比较大。但是由于确实是序列化了所有内容,所以可以说什么都可以传输,因此也更可用和可靠。

hessian序列化

hessian的实现机制是着重于数据,附带简单的类型信息的方法。就像Integer a = 1,hessian会序列化成I 1这样的流,I表示int or Integer,1就是数据内容。而对于复杂对象,通过Java的反射机制,hessian把对象所有的属性当成一个Map来序列化,包含了基本的类型描述和数据内容。而在序列化过程中,如果一个对象之前出现过,hessian会直接插入一个R index这样的块来表示一个引用位置,从而省去再次序列化和反序列化的时间。这样做的代价就是hessian需要对不同的类型进行不同的处理(因此hessian直接偷懒不支持short),而且遇到某些特殊对象还要做特殊的处理(比如StackTraceElement)。而且同时因为并没有深入到实现内部去进行序列化,所以在某些场合会发生一定的不一致,比如通过Collections.synchronizedMap得到的map。

hessian代码

maven依赖

1
2
3
4
5
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.38</version>
</dependency>

HessianSerializeUtil

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* @author shency
* @description: Hessian序列化工具类
* @date: 2019/10/24
*/
public class HessianSerializeUtil {
public static <T> byte[] serialize(T obj) {
byte[] bytes = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
HessianOutput hessianOutput = new HessianOutput(bos);
try {
// 注意,obj 必须实现Serializable接口
hessianOutput.writeObject(obj);
bytes = bos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}

return bytes;
}

public static <T> T deserialize(byte[] data) {
if (data == null) {
return null;
}
ByteArrayInputStream bis = new ByteArrayInputStream(data);
HessianInput hessianInput = new HessianInput(bis);
Object object = null;
try {
object = hessianInput.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return (T) object;
}
}

Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @author shency
* @description: TODO
* @date: 2019/10/24
*/
public class Solution {
public static void main(String[] args) {
User user = new User();
user.setName("husky");
byte[] bytes = HessianSerializeUtil.serialize(user);
System.out.println(bytes);
user = (User)HessianSerializeUtil.deserialize(bytes);
System.out.println(user.getName());
}
}

运行结果

image-20191226003634804

-------------本文结束感谢您的阅读-------------