序列化和反序列化原理分析

Java领域的对象如何传输

基于socket进行对象传输

当对象没有被序列化时,报异常(java.io.NotSerializableException: com.my.User
);通过对象实现 Serializable 接口就可以解决这个问题。

了解序列化的意义

序列化把对象-->可存储或可传输的过程,即对象-->字节数组,解决jvm的对象的持久化或跨服务传输的问题。
反序列化则相反

序列化的高阶认识

java原生序列化

通过ObjectOutputStream和ObjectInputStream来实现序列化和反序列化

    public <T> byte[] serialize(T obj) {
        ByteArrayOutputStream byteArrayOutputStream=
                new ByteArrayOutputStream();
        try {
            ObjectOutputStream outputStream=
                    new ObjectOutputStream(byteArrayOutputStream);

            outputStream.writeObject(obj);

            return  byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new byte[0];
    }
    public <T> T deserialize(byte[] data, Class<T> clazz) {
        ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(data);
        try {
            ObjectInputStream objectInputStream=
                    new ObjectInputStream(byteArrayInputStream);

            return (T) objectInputStream.readObject();

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

serialVersionUID 的作用

作用

来验证版本的一致性,反序列化serialVersionUID和实体类的serialVersionUID,若一致则可以进行反序列化,若不一致,则抛出异常InvalidCastException如这种场景:隐式声明序列化+序列化;修改本地实体类的内容;再进行反序列化会抛出InvalidCastException。

生成方法

1 隐式:编译器会默认生成一个
2 显示:private static final long serialVersionUID

生成规则

根据类名、接口名、成员方法及属性等来生成一个 64 位的哈希字段即是序列化,类似于指纹算法,只要内容发生修改,则UID就不一样。

Transient 关键字

若变量加上Transient,则阻止该变量序列化,在被反序列化后,变量会被初始化默认值。

绕开 transient 机制的办法

通过增加writeObject和readObject的方法来解决,该方法在ObjectInputStream 和 ObjectOutputStream通过反射调用

public class User {
    private static final long serialVersionUID = -434539422310062943L;
    private String name;
    private int age;

    private void writeObject(java.io.ObjectOutputStream s) throws IOException {
        s.defaultWriteObject();
        s.writeObject(name);
    }

    private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
        s.defaultReadObject();
        name=(String)s.readObject();
    }
}
image.png

序列化总结

Java 序列化只是针对对象的状态进行保存,至于对象中的方法,序列化不关心
当一个父类实现了序列化,那么子类会自动实现序列化,不需要显示实现序列化接口
当一个对象的实例变量引用了其他对象,序列化这个对象的时候会自动把引用的对象也进行序列化(实现深度克隆)
当某个字段被申明为 transient 后,默认的序列化机制会忽略这个字段
被申明为 transient 的字段,如果需要序列化,可以添加两个私有方法writeObject 和readObject

分布式架构下常见序列化技术

序列化的评价标准

序列化后的数据小,从而使传输效率高
其他语言可以识别和对接

简单了解各种序列化技术

XML 序列化框架介绍

可读性,跨语言,但序列化之后的字节码文件较大,适合性能要求不高的系统。实现方式有 XStream 和 Java 自带的 XML 序列化和反序列化两种

public class XStreamSerializer implements ISerializer{
    XStream xStream=new XStream(new DomDriver());

    @Override
    public <T> byte[] serialize(T obj) {
        return xStream.toXML(obj).getBytes();
    }

    @Override
    public <T> T deserialize(byte[] data, Class<T> clazz) {
        return (T)xStream.fromXML(new String(data));
    }
}
JSON 序列化框架

可读性,跨语言且相对于xml,json序列化后的字节码文件较小,企业应用广泛
JSON 序列化常用的开源工具有很多

  1. Jackson (https://github.com/FasterXML/jackson
  2. 阿里开源的 FastJson (https://github.com/alibaba/fastjon
  3. Google 的 GSON (https://github.com/google/gson)
    这几种 json 序列化工具中,Jackson 与 fastjson 要比 GSON 的性能要好,但是 Jackson、
    GSON 的稳定性要比 Fastjson 好。而 fastjson 的优势在于提供的 api 非常容易使用
public class FastJsonSeriliazer implements ISerializer{
    @Override
    public <T> byte[] serialize(T obj) {
        return JSON.toJSONString(obj).getBytes();
    }

    @Override
    public <T> T deserialize(byte[] data, Class<T> clazz) {
        return (T)JSON.parseObject(new String(data),clazz);
    }
}
Hessian 序列化框架

跨语言,性能更高的二进制序列化协议

public class HessianSerializer implements ISerializer{
    @Override
    public <T> byte[] serialize(T obj) {
        ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
        HessianOutput hessianOutput=new HessianOutput(outputStream);
        try {
            hessianOutput.writeObject(obj);
            return outputStream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return new byte[0];
    }

    @Override
    public <T> T deserialize(byte[] data, Class<T> clazz) {
        ByteArrayInputStream inputStream=new ByteArrayInputStream(data);
        HessianInput hessianInput=new HessianInput(inputStream);
        try {
            return (T)hessianInput.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
Protobuf 序列化框架

跨语言和序列化后的字节码的文件最小,性能最高。但使用起来比较麻烦。

Protobuf 序列化的原理

name=mic,age=300的序列化后的结果:10 3 77 105 99 16 -84 2

对数字的压缩
image.png

把300变成:-84 、2

字符如何转化为编码

“Mic”这个字符,需要根据 ASCII 对照表转化为数字。
M =77、i=105、c=99
所以结果为 77 105 99

存储格式

protobuf 采用 T-L-V 作为存储方式


image.png

tag 的计算方式是 field_number(当前字段的编号) << 3 | wire_type

为什么空间如此小

对值进行压缩
没有了字段名,通过位置来知道字段名

序列化技术的选型

技术层面

序列化空间开销,也就是序列化产生的结果大小,这个影响到传输的性能
序列化过程中消耗的时长,序列化消耗时间过长影响到业务的响应时间
序列化协议是否支持跨平台,跨语言。因为现在的架构更加灵活,如果存在异构系统通信需求,那么这个是必须要考虑的
可扩展性/兼容性,在实际业务开发中,系统往往需要随着需求的快速迭代来实现快速更新,
这就要求我们采用的序列化协议基于良好的可扩展性/兼容性,比如在现有的序列化数据结构中新增一个业务字段,不会影响到现有的服务
技术的流行程度,越流行的技术意味着使用的公司多,那么很多坑都已经淌过并且得到了
解决,技术解决方案也相对成熟学习难度和易用性

选型建议

对性能要求不高的场景,可以采用基于 XML 的 SOAP 协议
对性能和间接性有比较高要求的场景,那么 Hessian、Protobuf、Thrift、Avro 都可以。
基于前后端分离,或者独立的对外的 api 服务,选用 JSON 是比较好的,对于调试、可读性都很不错
Avro 设计理念偏于动态类型语言,那么这类的场景使用 Avro 是可以的

各个序列化技术的性能比较

https://github.com/eishay/jvm-serializers/wiki

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 224,642评论 6 522
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,168评论 3 402
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 171,809评论 0 366
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,921评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 69,924评论 6 399
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,415评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,794评论 3 428
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,765评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,297评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,331评论 3 345
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,458评论 1 354
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,065评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,777评论 3 337
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,233评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,366评论 1 275
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,001评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,524评论 2 365

推荐阅读更多精彩内容