序列化

Android序列化之 子类与父类

目录

  1. Java序列化之Serializable
    1. Java语言实现Serializable
    2. kotlin语言实现Serializable
  2. Android序列化之parcelable
    1. Java语言实现Parceable
    2. Kotlin语言实现Parcelable
# Java序列化之Serializable

注意事项:

  1. 序列化时,只对对象的状态进行保存,不会对对象的方法进行保存
  2. 当一个父类实现序列化,子类自动实现序列化,不需要再次实现Serializable接口,但是,子类实现序列化,父类未实现序列化,父类数据不被保存
  3. 当一个对象的实例有变量引用其他对象,序列化该对象时也会把所引用对象进行序列化,如果引用对象没有实现序列化,则会抛出NotSerializableException异常
  4. 并非所有的对象都可以序列化
    1. 安全方面的原因,比如一个对象拥有private,public等field,对于一个要传输的对象,比如写到文件,或者进行rmi传输等,在序列化进行传输的过程中,这个对象的private等域是不受保护的
    2. 资源分配方面的原因,比如socket,thread类,如果可以序列化,进行传输或者保存,也无法对他们进行重新的资源分配。
  5. serialVersionUID对类的标识,在序列化与反序列化的时候使用,序列化时会将此标识写入到序列化数据中,反序列化时会检测此标识,如果标识一致就说明序列化的类和当前类的版本是相同的,可以成功过反序列化,否则就说明当前类和序列化的类相比发生了某些变换,比如变量的数量、类型可能发生了改变,这个时候是无法正常反序列化的,因此会抛出java.io.InvalidClassException。
  6. 如果不指定serialVersionUID则系统就会根据当前类的结构生成serialVersionUID,如果是两个版本的应用只是添加或删除了变量,而没有发生类似改变了类名,修改了类变量类型的非常规性改变则仍可反序列化成功

由上可知,父类实现了序列化编写一个可序列化的子类,可自动实现。那么父类未实现序列化子类该如何实现序列化。下面我们就来讲述这件事情。

对象被序列化的候其实就是将对象写入到文件中,写入的时候会调用writeObject(java.io.ObjectOutputStream out)方法,其中out就是写入的文件流,所以将想要保存的父类变量通过out写入文件就可以正常的实现序列化。

读取的时候会调用readObject(java.io.ObjectInputStream in)方法,其中in就是读取的文件流,从中就可以拿到序列化的时候写入的数据。

除此之外父类还需要实现一个无参的构造函数,否则会crash

例子

public abstract class SuperC {
	int supervalue;

	public SuperC(int supervalue) {
  	  this.supervalue = supervalue;
	}

	public SuperC() {
	 }//增加一个无参的constructor

	public String toString() {
    	return "supervalue: " + supervalue;
	}
}


public class SubC extends SuperC implements Serializable {
	int subvalue;

	public SubC(int supervalue, int subvalue) {
   	 super(supervalue);
  	  this.subvalue = subvalue;
	}

	public String toString() {
 	   return super.toString() + " sub: " + subvalue;
	}

	private void writeObject(java.io.ObjectOutputStream out)
        throws IOException {
    	out.defaultWriteObject();//先序列化对象
    	out.writeInt(supervalue);//再序列化父类的域
	}

	private void readObject(java.io.ObjectInputStream in)
        throws IOException, ClassNotFoundException {
    	in.defaultReadObject();//先反序列化对象
    	supervalue = in.readInt();//再反序列化父类的域
	}
}	

由上可知,如果父类未实现序列化,子类需要实现序列化,那么父类必须存在一个无参的构造函数,父类的变量,子类需要负责序列化

## Java语言实现Serializable public class Person implements Serializable { private static final long serialVersionUID = 123456789L;
	private int name;

	public Person() {
   	 	
	}

	public String getName(){
		return name;
	}
	
	public void setName(String name){
		this.name = name;
	}
}
## kotlin语言实现Serializable class Person() : Serializable{ companion object { private const val serialVersionUID = 123456789L }
	var name: String? = null
}
# Android序列化之Parcelable

对于Android平台来说,还有一个自己的序列化机制:Parcelable,就性能而言Parcelable比Serializable要优胜很多,因为Parcelable毕竟是亲儿子,Android在底层给起做了优化,至于做了什么优化,下面我们再详细阐述,在此加一句,Google对自己的语言kotlin在1.1.4版本中新加了一个@Parcelize注解,用来快速的实现Parcelable序列化,可见Google对自己语言的重视。

Parcelable对于子类与父类的序列化机制跟Serializable是一样的

讲一讲Parcelable中的几个重要方法

writeToParcel

	/**
     * Flatten this object in to a Parcel.
     * 
     * @param dest The Parcel in which the object should be written.
     * @param flags Additional flags about how the object should be written.
     * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
     */
    public void writeToParcel(Parcel dest, @WriteFlags int flags);

该方法通过参数传回一个parcel对象,你需要的就是将要序列化的数据写入parcel中,Flags可能是0或者PARCELABLE_WRITE_RETURN_VALUE亦或者PARCELABLE_ELIDE_DUPLICATES

describeContents

	/**
 	* Describe the kinds of special objects contained in this Parcelable
 	* instance's marshaled representation. For example, if the object will
 	* include a file descriptor in the output of {@link #writeToParcel(Parcel, int)},
 	* the return value of this method must include the
 	* {@link #CONTENTS_FILE_DESCRIPTOR} bit.
 	*  
 	* @return a bitmask indicating the set of special object types marshaled
 	* by this Parcelable object instance.
 	*/
	public @ContentsFlags int describeContents();

通过方法名就可以知道这个方法是负责文件描述的,通过阅读注释可以知道当文件在writeToParcel方法中传入的数据包含特殊类型的对象例如一个文件描述符,那么就必须包含CONTENTS_FILE_DESCRIPTOR位掩码。光看注释和解释是不是云里雾里的,那是肯定的,毕竟这涉及到文件类型封装,没有接触的小伙伴必然会蒙。其实可以这么理解,如果你所使用的parcel对象需要特殊的描述类型,那么这个方法你就需要传入CONTENTS_FILE_DESCRIPTOR才能将使用该描述。

Creator

	/**
 	* Interface that must be implemented and provided as a public CREATOR
 	* field that generates instances of your Parcelable class from a Parcel.
 	*/
	public interface Creator<T> {
    /**
    * Create a new instance of the Parcelable class, instantiating it
    * from the given Parcel whose data had previously been written by
    * {@link Parcelable#writeToParcel Parcelable.writeToParcel()}.
    * 
    * @param source The Parcel to read the object's data from.
    * @return Returns a new instance of the Parcelable class.
    */
    public T createFromParcel(Parcel source);
    
    /**
     * Create a new array of the Parcelable class.
     * 
     * @param size Size of the array.
     * @return Returns an array of the Parcelable class, with every entry
     * initialized to null.
     */
    public T[] newArray(int size);
	}

可以看到Creator是一个接口,定义了createFromParcel和newArray两个方法,它是作为一个加载器存在,当反序列化的时候就需要用到它,在实现Parcelable序列化的时候必须在要实现序列化的类中添加一个名称为CREATOR的内部静态变量继承Parcelable.Creator

例:

public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {
    @Override
    public Person createFromParcel(Parcel source) {
        return new Person(source);
    }

    @Override
    public Person[] newArray(int size) {
        return new Person[size];
    }
};

createFromParcel

 /**
  * Create a new instance of the Parcelable class, instantiating it
  * from the given Parcel whose data had previously been written by
  * {@link Parcelable#writeToParcel Parcelable.writeToParcel()}.
  * 
  * @param source The Parcel to read the object's data from.
  * @return Returns a new instance of the Parcelable class.
  */
 public T createFromParcel(Parcel source);

可以看到createFromParcel就是将序列化打包后的数据重新转成需要的对象。

newArray

/**
 * Create a new array of the Parcelable class.
 * 
 * @param size Size of the array.
 * @return Returns an array of the Parcelable class, with every entry
 * initialized to null.
 */
public T[] newArray(int size);

需要创建对应类的数组,数组的元素为null。

## Java语言实现Parceable
public class Person implements Parcelable {
	private String name;

	public String getName() {
   	 return name;
	}

	public void setName(String name) {
    	this.name = name;
	}


	@Override
	public int describeContents() {
    	return 0;
	}

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

	public Person() {
	}

	protected Person(Parcel in) {
    	this.name = in.readString();
	}

	public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {
    @Override
    	public Person createFromParcel(Parcel source) {
        	return new Person(source);
    	}

    	@Override
    	public Person[] newArray(int size) {
        	return new Person[size];
    	}
	};
}
## Kotlin语言实现Parcelable
@Parcelize
@SuppressLint("ParcelCreator")
data class Person(var name: String) : Parcelable

区别

  1. Parcelable是Android特有的,Serializable是Java的
  2. Parcelable性能比Serializable好,Serializable是通过IO实现的序列化,存储在硬盘上;Parcelable是读写在内存上,速度上就要优于Serializable,而且Serializable的实现是利用了反射技术,产生了大量的临时对象
  3. 使用Java语言的话Parcelable代码量比Serializable大很多,但是使用Kotlin语言使用Parcelable要远远比使用Serlizable代码量小
  4. 如果要将数据存储到磁盘上,在外界有变化的情况下Parcelable不能很好的保证数据的持续性,此时可以使用Serializable虽然性能低点

原则

  1. 尽量使用Parcelable进行序列化保证性能,而且Java有专门的差价进行Parcelable序列化已经很大的解决了代码量上的问题
  2. 如果涉及到在内存变动,外部持续编写等情况要使用Serializable以保证数据的准确性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值