一篇文章弄懂Parcelable和Serializable

阅读完本篇文章会知道如下三点:

1.Parcelable和Serializable的作用?

2.通过例子来区分Parcelable和Serializable,两者的流程是?

3.两者的区别?

首先知道什么是序列化:

官方的概念是:

序列化:把对象转换为字节序列的过程

反序列化:把字节序列恢复为对象的过程

通俗的理解:

序列化就是将对象的状态信息转换为可以存储或传输的形式过程。在java中使用Serializable关键字实现序列化。序列化的目的是以某种存储形式使自定义对象持久化,简单的理解就是:将对象从一个地方传递到另一个地方。那么这时我们可以得知需要序列化的情况:

1.当我们把内存对象状态保存到一个文件中或者数据库中

2.当用套接字在网络上传送对象

3.当通过RMI传输对象

后面两种也是网上所查。

1.Parcelable和Serializable的作用

Parcelable是Android独有提供的系列化接口,而Serializable是java提供的序列化接口。Parcelable只能在Android中使用,Serializable可以在使用java语言的地方使用。

2.通过例子来区分Parcelable和Serializable,两者的流程是?

下面分别举出两个例子:

Serializable

public class People implements Serializable{
    
	//名字
	private String name;
    //年龄
	private int age;
	//静态变量
	private static String test = "1234";
	
	private static final long serialVersionUID = 1L;
	
	public People() {
		System.out.println("进入默认的构造方法");
	}
	
	private People(int age) {
		System.out.println("私有构造方法:年龄"+age);
		
	}

	

	public String getName() {
		return name;
	}

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

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
	
	
	@Override
	public String toString() {
		return "People [name=" + name + ", age=" + age + ",test="+ test +"]";
	}

}

注意里面是有一个静态变量。下面将对象输出到一个文件中:

/**
	 * 序列化函数
	 * 
	 * 
	 */
	private static void serializePeople() {
		People people = new People();
		people.setAge(24);
		people.setName("paul");
		 try {
			ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream(new File("d:/people.txt")));
		    output.writeObject(people);
		    output.close();
		 } catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		
	}

实践结果是可以的。

下面进行反序列化:

	/**
	 * 反序列化
	 * 
	 * 
	 * @return 返回对象
	 */
	private static People deseriallizePeople() {
		try {
			ObjectInputStream oInput = new ObjectInputStream(new FileInputStream(new File("d:/people.txt")));
			try {
				People person = (People) oInput.readObject();
				return person;
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

执行函数结果显示:

结果显示反序列化成功,但是上面说过有一个静态变量,现在我们不知道是否到底有没有序列化,下面验证一下:下面将test改为:private static String test = "12";然后反序列一下看看结果。

可以发现之前设定的test值1234并没有将之后设定的test值12给覆盖到,也就是说静态变量不参与序列化!

另外注意到类里面有serialVersionUID这个值。

这里重点说一下:其实不指定serialVersionUID也是可以实现序列化,那么加上这个字段又是有什么作用呢?其实这个serialVersionUID是用来辅助序列化和反序列化过程的,因为原则上序列化后的数据中serialVersionUID只有和当前类的serialVersionUID相同时才能够正常地被反序列化。

serialVersionUID的详细工作机制是:

1.序列化的时候系统会去把当前类的serialVersionUID写入序列化的文件中(也有可能是其他中介)

2.当反序列化的时候系统会检测文件中的serialVersionUID,看它是否和当前类的serialVersionUID一致,如果一致的时候就说明序列化的类的版本和当前类的版本是相同的,这个时候就可以成功反序列化;否则就说明当前类和序列化的类相比发生了某些变换。这时候是无法正常反序列化的。

那么serialVersionUID这个值应该怎么指定?

一般来说,我们应该手动指定serialVersionUID的值,如1L,如果不手动指定serialVersionUID的值,反序列化时当前类有所改变,如增加或者删除某些成员变量,系统就会重新计算当前类的hash值并赋值给serialVersionUID,这个时候当前类的serialVersionUID就和序列化的数据中的serialVersionUID不一致于是就有反序列化失败。

当手动指定它的值,就很大程度上避免了反序列化过程的失败,比如版本迭代中,删除或者增加一些成员变量,这时候还是反序列化成功。

因此通常给serialVersionUID指定为1L就可以了。

注意:如果类发生结构上的变化上时:如修改了类名,修改了成员变量的类型,虽然serialVersionUID验证通过了,但是反序列化过程还是会失败的。

下面讲讲如何自定义一个类让其实现Parcelable?

自定义一个类让其实现Parcelable接口,主要分为三部分:

1.重写该接口的writeToParcel(Paecel out,int flags)

2.重写describeContents()

3.添加一个Parcelable.Creator类型的字段

例子如下:

public class PeopleParcel implements Parcelable {
    public String name;
    public boolean isMan;
    public int age;

    public String getName() {
        return name;
    }

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

    public boolean isMan() {
        return isMan;
    }

    public PeopleParcel(String name, boolean isMan, int age) {
        this.name = name;
        this.isMan = isMan;
        this.age = age;
    }


    public void setMan(boolean man) {

        isMan = man;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }


    /**
     * 实现反序列化,需要有一个参数类型为Parcel的构造方法
     * @param in
     */
    private PeopleParcel(Parcel in){
        name = in.readString();
        age = in.readInt();
        isMan = in.readInt() == 1;
    }



    public static final Creator<PeopleParcel> CREATOR  = new Creator<PeopleParcel>(){

        /**
         * 根据Parcel中来创建一个原始对象
         * @param source
         * @return
         */
        @Override
        public PeopleParcel createFromParcel(Parcel source) {
            return new PeopleParcel(source);
        }

        /**
         * 返回指定长度的原始对象数组
         * @param size
         * @return
         */
        @Override
        public PeopleParcel[] newArray(int size) {
            return new PeopleParcel[size];
        }
    };

    /**
     * 返回当前对象的内容描述。如果含有文件描述符,返回1,否则返回0,几乎所有情况都是返回0
     * @return
     */
    @Override
    public int describeContents() {
        return 0;
    }

    /**
     * 调用Parcel的一系列方法来把数据写入Parcel中
     * @param dest
     * @param flags
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
        dest.writeInt(isMan ? 1 : 0);
    }
}

3.两者的区别?

Android中的序列化的例子就是上面所示。

在Android中两者都能实现序列化并能用Intent间的数据传递,难么究竟还有什么区别呢?

1.Serializable是java中的序列化接口,使用起来简单但是开销大,因为序列化过程需要大量的I/O操作

2.Pacelable是Android中的序列化方式,虽然使用起来麻烦一点,但是效率高。实际上如何打包和解包的工作自己定义,序列化的这些操作完全交个底层实现。

猜你喜欢

转载自blog.csdn.net/qq_33453910/article/details/81007416