java进阶篇07、序列化--Serializable、Parcelable和Json

1,434 阅读6分钟

一、序列化和反序列化

序列化是指将数据结构或者对象转换为二进制串的过程;

反序列化是指将序列化生成的二进制串转换成数据结构或者对象的过程;

序列化和反序列化的目的主要是方便数据进行网络传输和数据持久化;

常见的序列化和反序列化数据格式有JSON和XML,在android中json应用最为广泛;

二、Serializable接口

此接口是java提供的序列化接口,它是一个空接口;一个类如果想要被ObjectOutputStream序列化,被ObjectInputStream反序列化,则需要实现Serializable接口;真正进行序列化和反序列化操作是通过writeObject方法和readObject方法;

1、serialVersionUID唯一标识了一个可序列化的类,我们实现自己的可序列化类时最好自己手动定义一个serialVersionUID变量,并设为private和final的;如果我们没有手动设置此值,那么在序列化之后修改了某个类,在反序列化的时候就会发生错误,因为修改了类后程序自动计算的serialVersionUID值跟以前的不同,就会造成类版本不兼容问题。

2、可序列化的类中,未实现Serializable接口的属性无法进行序列化和反序列化;当反序列化一个类时,他的不可序列化的属性将会调用其无参构造方法重新创建,因此这个属性的无参构造函数必须存在,否则就会报错;

3、一个实现了序列化的类,那么它的子类也是可以序列化的;

4、用transient关键字修饰的成员变量不参与序列化,在反序列化时该字段被设为初始值;

5、静态变量属于类不属于对象,因此不会参与序列化;对象序列化保存的是对象的状态,也就是他的成员变量,因此序列化不会关注静态变量;

6、Externalizable接口继承自Serializable接口,我们可以通过实现此接口使我们的类可序列化,这个接口有两个方法需要实现,writeExternal和readExternal方法,我们在这两个方法中实现我们自己的序列化逻辑,比如说只想序列化某几个字段,就可以写在这两个方法中;

三、readObject/writeObject流程分析:以writeObject为例

真正进行序列化和反序列化操作是通过writeObject方法和readObject方法,下面是writeObject的流程分析;

1、writeObject()方法如下所示,因为在ObjectOutputStream的构造方法中将enableOverride设为fasle,所以会调用到writeObject0()方法;

public final void writeObject(Object obj) throws IOException {
	if (enableOverride) {
	    writeObjectOverride(obj);
	    return;
	}        
	writeObject0(obj, false);
	、、、
}

2、writeObject0如下所示,因为我们的类实现了Serializable接口,因此在writeObject0中会调用writeOrdinaryObject;

private void writeObject0(Object obj, boolean unshared) throws IOException {
    、、、
    else if (obj instanceof Serializable) {
        writeOrdinaryObject(obj, desc, unshared);
    }
}

3、writeOrdinaryObject方法如下所示,如果我们的类是实现自Externalizable接口,就会调用writeExternalData方法,否则会调用writeSerialData方法;

private void writeOrdinaryObject(Object obj,ObjectStreamClass desc,boolean unshared){
    if (desc.isExternalizable() && !desc.isProxy()) {
        writeExternalData((Externalizable) obj);
    } else {
        writeSerialData(obj, desc);
    }
}

4、writeSerialData方法如下所示,如果我们在自己的类中重写了WriteObject方法,那么就会调用我们自己重写的;如果没有重写,那么就会调用defaultWriteFields方法对各个字段进行序列化;而在defaultWriteFields方法中又会根据基本类型字段和对象字段分别处理;

private void writeSerialData(Object obj, ObjectStreamClass desc){
    if (slotDesc.hasWriteObjectMethod()) {
        、、、
    }else {
        defaultWriteFields(obj, slotDesc);
    }
}

5、通过源码分析发现其实我们可以重写readObject,writeObject,readResolve,writeReplace方法,我们可以在实现了Serializable接口的类中重写这四个方法,用来定义我们自己的序列化逻辑;通过看源码可以发现调用顺序是writeReplace先于writeObject,readResolve后于readObject;

四、Parcelable接口

Serializable是Java为我们提供的一个标准化的序列化接口,而Parcelable是Android为我们提供的序列化的接口,Parcelable的使用相对复杂一点,但是效率高很多;Parcelable是AndroidSDK提供的,它是基于内存的,由于内存读写速度高于硬盘,因此Android中的跨进程对象的传递一般使用Parcelable;

使用:实现Parcelable接口,需要重写writeToParcel方法;需要创建一个Parcelable.Creator对象;

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(this.name);
    dest.writeFloat(this.score);
}

public static final Parcelable.Creator<Course> CREATOR = new Parcelable.Creator<Course>()

典型问题总结:

1、Parcelable和Serializable对比:

Serializable通过IO对硬盘进行操作,速度较慢,大小不受限制,大量使用反射产生碎片;Parcelable直接在内存操作,效率高,大小一般不能超过1M;

2、Android里面为什么要设计出Bundle而不是直接用Map结构

在Android中如果使用Intent来携带数据的话,需要数据是基本类型或者是可序列化类型,HashMap使用Serializable进行序列化,而Bundle则是使用Parcelable进行序列化。而在Android平台中,更推荐使用Parcelable实现序列化,所以系统封装了Bundle;

3、为何Intent不能直接在组件间传递对象而要通过序列化机制?

当我们使用Intent启动其他组件时,此时需要在不同进程间传递数据,我们知道对象不能在进程之间直接传输,所以我们需要将其序列化,在安卓中更推荐Parcelable方式;

五、基本的Json解析方式

json是一种轻量级的数据交换格式;读写速度快,解析简单,轻量级,独立于语言和平台,描述性较好;通过解析器可以将json与javabean互转,实现序列化和反序列化,常用的解析器有Gson和FastJson;

json常用解析方式基本使用

Gson解析:

只需简单的调用如下代码就可以将JSON数据自动解析成一个Person对象了:

Gson gson = new Gson();

Person person = gson.fromJson(jsonData,Person.class);

如果需要解析的是一段JSON数组,我们需要借助TypeToken将期望解析成的数据类型传入到fromJson中:

List list = gson.fromJson(jsonData,new TypeToken<List(){}.getType());

如果需要将person对象转换为json数据,只需要简单的toJson方法即可:

String jsonData = gson.toJson(person);

FastJson解析:

如需将javabean转换为json数据,只需调用如下方法即可,参数也可以传集合:

String jsonData = JSON.toJSONString(person);

如果想要将json数据转换为javabean,只需调用以下方法即可:

Person person = JSON.parseObject(jsonData,Person.class);

如果想要转换为更复杂的对象,比如集合等,可以使用如下方法:

List list = JSON.parseObject(jsonData,new TypeReference<List>(){});