Java序列化与反序列化的深度思考

目录

1、序列化与反序列化的作用

2、序列化协议

2.1 JDK序列化协议

2.1.1  Serializable VS Externalizable(待完成)

2.2 Google ProtocolBuf 协议

2.3 Kryo 协议

2.4   XML协议

2.5  JSON协议         

3、序列化与单例模式


1、序列化与反序列化的作用

序列化:将堆中对象转化成可以存储或网络传输的二进制字节序列的过程。

反序列化:将来自于网络传输或者存储的字节序列恢复成Java对象的过程。

当Java对象需要进行本地文件存储时,需要将Java对象进行序列化;
当Java对象需要进行网络传输,如 rpc调用、数据库持久化,需要将Java对象进行序列化;

2、序列化协议

2.1 JDK序列化协议

下面是JDK序列化的一个示例:

package com.autocoding.serializable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class JdkSerializer {
	public static void serialize(Student student) throws FileNotFoundException, IOException {
		ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("D:\\student.txt")));
		oo.writeObject(student);
		oo.close();
	}
	public static Student deserialize() throws IOException, Exception {
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("D:\\student.txt")));
		Student student = (Student) ois.readObject();
		return student;
	}
	public static void main(String[] args) {
		Student student = new Student();
		student.setId("1000");
		student.setName("李桥");
		try {
			// 序列化
			JdkSerializer.serialize(student);
			// 反序列化
			Student serializeStudent = JdkSerializer.deserialize();
			System.out.println(serializeStudent.getName());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	private static class Student implements Serializable {
		private static final long serialVersionUID = 8888L;
		private String id;
		private String name;
         //构造器访问控制符为私有的,埋下一个伏笔
		privateStudent() {
		}
		public String getId() {
			return id;
		}
		public void setId(String id) {
			this.id = id;
		}
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
	}
}

下面是序列化到文件 D:\\student.txt 的内容:

比较粗略的可以看到序列化之后,存储了对象的类型信息,字段的类型信息。

2.1.1  Serializable VS Externalizable(待完成)

2.2 Google ProtocolBuf 协议

Protocol Buff是谷歌推出的一种序列化协议,是一个非常高效的序列化协议。相对于Java这种数据类型固定长度的序列化(int 4字节, long 8字节), PB提供了可伸缩性的数据类型(int 1-5字节)。
当数据比较小的时候int只占用1个字节, 而大部分场景下, 数据都是很小的, 不然jdk本身也不会缓存 -127~128了。
         参考文档:Protocol Buffer序列化对比Java序列化.
                           性能最好的序列化反序列化,Protobuf的用法

优点:1、序列化后码流小,性能高
           2、通过标识字段的顺序,可以实现协议的前向兼容
           3、结构化数据存储格式(XML JSON等),结构化的文档更容易管理和维护
          

缺点:1、需要依赖于工具生成代码
           2、支持的语言相对较少,官方只支持Java 、C++ 、Python
           3、序列化数据只包含数据本身以及类的结构,不包括类型标识和程序集信息


使用场景: 对性能要求高的RPC调用

2.3 Kryo 协议

          Kryo是一种非常成熟的序列化实现,已经在Twitter、Groupon、 Yahoo以及多个著名开源项目(如Hive、Storm)中广泛的使用。而FST是一种较新的序列化实现,目前还缺乏足够多的成熟使用案例,但它还是非 常有前途的,
下面我们比较下,java原生序列化Kryo序列化性能比较。Kryo的性能是JDK序列化协议性能的10倍-100倍之间。

         参考文档:java原生序列化和Kryo序列化性能比较

2.4   XML协议

优点:1、人机可读性好,可指定元素或特性的名称
           2、数据可以有层次结构
          

缺点:1、只能序列化属性和字段、不能序列化方法
           2、文件庞大,文件格式复杂,传输占带宽
           3、序列化数据只包含数据本身以及类的结构,不包括类型标识和程序集信息


使用场景: 配置文件存储数据

xml类库主要有:dom4j、Jdom、xstrem                     参考文档: Java 主流开源类库解析 XML

2.5  JSON协议         

优点:1、数据格式比较简单,易于读写
           2、序列化后数据较小,可扩展性好,兼容性好
           3、与XML相比,其协议比较简单,解析速度比较快

缺点:1、数据的描述性比XML差
           2、不适合性能要求为ms级别的情况
           3、额外空间开销比较大


使用场景: 传输数据量相对小,实时性要求相对低(例如秒级别)的服务

3、序列化与单例模式

         在将二进制流反序列化时,通过二进制中的类型信息,进行反射,获取Class对象,然后调用 newInstance()方法来创建对象,请注意newInstance()方法会调用该Class对象的无参构造器(private、public都可以),这样就创建了一个
对象实例,这样很可能会破坏单例模式中的该类只能保留一个实例的约束,为了保证单例模式真正的安全,我们一般使用枚举类型来实现真正的单例模式。

      在effective java(这本书真的很棒)中说道,最佳的单例实现模式就是枚举模式。利用枚举的特性,让JVM来帮我们保证线程安全和单一实例的问题。除此之外,写法还特别简单。

public enum Singleton {
    INSTANCE;
    public void doSomething() {
        System.out.println("doSomething");
    }
}

     使用枚举来实现单例模式可以很好的解决这种因为反射而带来的多例风险,所以个人推荐使用枚举来实现单例模式。

猜你喜欢

转载自blog.csdn.net/s2008100262/article/details/112940126