漫谈序列化—使用、原理、问题(上)

简介: 天天跟我说给我介绍对象对象,对象在哪里?哪里有对象?

前言


天天跟我说给我介绍对象对象,对象在哪里?哪里有对象?


你倒是把对象拿给我看看啊!


拿去拿去 :


{
 "name": "小丽",
 "age": "22",
 "sex": "女"
}


我去~


序列化概念


说到对象,是一个比较宽泛的概念,简单的说,他就是类的一个实例,有状态和行为,存活在内存中,一旦JVM停止运行,对象的状态也会丢失。


那么如何将这个对象当前状态进行一个记录,使其可以进行存储和传输呢?这就要用到序列化了:


序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程


比如一个User对象,名字为小丽,年龄22,性别为女。现在要把这个User对象保存下来,不然要是这个对象被别人改成了男可咋办。


所以我们就可以把它当前的状态信息转化成一种固定的格式,比如json格式


{
 "name": "小丽",
 "age": "22",
 "sex": "女"
}


所以上述的例子就是一个序列化过程,本身这个User对象存活在内存中,是无法直接进行数据持久化的,所以我们需要一些序列化的方式让它可以进行保存传输:


比如xml、JSON、Protobuf、Serializable、Parcelable,这些都是可以进行序列化的方式。


所以关于序列化我们就有很多问题了:


  • 在java有Serializable的前提下,Android为什么设计出了Parcelable?
  • Parcelable一定比Serializable快吗?
  • 为什么Java提供了Serializable的序列化方式,而不是直接使用json或者xml?
  • Serializable、Parcelable、Json等序列化方式我们该怎么选择?


带着这些问题,我们去看看序列化的世界。


Serializable


先说说Java中自带的序列化方式——Serializable


Serializable是java.io包中定义的、用于实现Java类的序列化操作而提供的一个语义级别的接口


只要我们实现Serializable接口,那么这个类就可以被ObjectOutputStream转换为字节流,也就是进行了序列化。


使用


java:


public class User implements Serializable {
    private static final long serialVersionUID=519067123721561165l;
    private int id;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
}


kotlin:


data class User( 
    val id: Int
) : Serializable


serialVersionUID


这个变量如果不写,系统也会自动生成。它的作用在于标示这个数据对象的一致性。

当序列化的时候,系统会把当前类的serialVersionUID写入序列化的文件中,当反序列化的时候会去检测这个serialVersionUID,看他是否和当前类的serialVersionUID一致,一样则可以正常反序列化,如果不一样就会报错了。


如果我们不写的话,在我们修改类的某些属性之后,serialVersionUID就会改变。


所以我们手动指定serialVersionUID后,就能在修改类之后,让系统认识序列化的过程中标示这是同一个类,从而保证最大限度来恢复数据。


原理


在Serializable的注释中有提到,如果要想在序列化过程中做一些特殊的操作,可以实现这几个特殊方法:


  • writeObject(),负责写入对象的特定类,以便相应的readObject方法可以恢复它
  • readObject(),负责从流中读取并恢复类字段


所以这两个方法其实就是Serializable实现的关键。首先看看写入方法writeObject(伪代码):


private void writeObject(){
    //获取类的描述信息ObjectStreamClass(里面包含了类名称、类字段、serialVersionUID等,用到大量反射)
     desc = ObjectStreamClass.lookup(cl, true);
     //写入元数据TC_OBJECT,代表是一个新对象
     bout.writeByte(TC_OBJECT);
     //写入描述信息(从父类写到子类)
     writeClassDesc(desc, false);
     //写入serialVersionUID,serialVersionUID为空的情况下,序列化机制就会调用一个函数根据类内部的属性等计算出一个hash值
     getSerialVersionUID();
     //执行JVM的序列化操作
     defaultWriteFields();
}
private void defaultWriteFields(Object obj, ObjectStreamClass desc){
    //写入基本数据类型
    bout.write(primVals, 0, primDataSize, false);
    //写入引用数据类型(又重新调用了writeObject方法)
    Object[] objVals = new Object[desc.getNumObjFields()];
    for (int i = 0; i < objVals.length; i++) {
        writeObject(objVals[i],fields[numPrimFields + i].isUnshared());
    }
}


写入数据的流程基本就这些,可以看到Serializable序列化的过程,其实就是一个写入流的过程。然后就可以根据情况将二进制流保持为文件,或者包装成ByteArrayOutStream写入到内存中进行传输。


所以Serializable使用的范围比较广,可以作为文件保存下来,也可以作为二进制流对象用于内存中的传输。但是由于用到反射、IO,而且大量的临时变量会引起频繁的GC,所以效率不算高。


所以,为了提高在Android中对象传输的效率呢,Android就采用了新的序列化方式——Parcelable


Parcelable


Parcelable是Android为我们提供的序列化的接口,是为了解决Serializable在序列化的过程中消耗资源严重,而Android本身的内存比较紧缺的问题,但是用法较为繁琐,主要用于内存中数据的传输。


使用


java:


public class User implements Parcelable {
    private int id;
    protected User(Parcel in) {
        id = in.readInt();
    }
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(id);
    }
    @Override
    public int describeContents() {
        return 0;
    }
    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }
        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
}


kotlin:


androidExtensions {
    experimental = true
}
@Parcelize
data class User(val name: String) : Parcelable


目录
相关文章
|
6月前
|
缓存 Java
JDK序列化原理问题之Fury如何实现与JDK序列化100%兼容的如何解决
JDK序列化原理问题之Fury如何实现与JDK序列化100%兼容的如何解决
116 0
|
6月前
|
Java
JDK序列化原理问题之Hessian框架不支持writeObject/readObject方法如何解决
JDK序列化原理问题之Hessian框架不支持writeObject/readObject方法如何解决
|
6月前
|
Java
JDK序列化原理问题之在JDK序列化中不同JDK版本字段不一致的情况如何解决
JDK序列化原理问题之在JDK序列化中不同JDK版本字段不一致的情况如何解决
104 0
|
6月前
|
自然语言处理 JavaScript 前端开发
JDK序列化原理问题之FuryJDK序列化性能问题的如何解决
JDK序列化原理问题之FuryJDK序列化性能问题的如何解决
105 2
|
9月前
|
缓存 自然语言处理 JavaScript
万字长文深度解析JDK序列化原理及Fury高度兼容的极致性能实现
Fury是一个基于JIT动态编译的高性能多语言原生序列化框架,支持Java/Python/Golang/C++/JavaScript等语言,提供全自动的对象多语言/跨语言序列化能力,以及相比于别的框架最高20~200倍的性能。
168813 12
|
9月前
|
存储 XML JSON
日常小知识点之序列化结构(protobuf使用及简单原理)
日常小知识点之序列化结构(protobuf使用及简单原理)
235 0
|
9月前
|
存储 Java 开发工具
[Android]序列化原理Parcelable
[Android]序列化原理Parcelable
149 0
|
9月前
|
存储 Java Android开发
[Android]序列化原理Serializable
[Android]序列化原理Serializable
105 0
|
9月前
|
安全 Java
Java单例---序列化破坏单例模式原理解析(二)
Java单例---序列化破坏单例模式原理解析
62 0
|
9月前
|
Java
Java单例---序列化破坏单例模式原理解析(一)
Java单例---序列化破坏单例模式原理解析
98 0