Java 对象序列化
对象序列化机制允许把内存中的Java对象包装成为与平台无关的二进制流,从而允许把这种二进制流持久保存在磁盘上或者通过网络将这种二进制流传输到另外的节点。然后再利用反序列化,将Java对象对IO流中恢复,只能将支持 java.io.Serializable 接口的对象写入流中。每个 serializable 对象的类都被编码,编码内容包括类名和类签名、对象的字段值和数组值,以及从初始对象中引用的其他所有对象的闭包。
概念
序列化:把Java对象转换为字节序列的过程。
反序列化:把字节序列恢复为Java对象的过程。
用途
对象的序列化主要有两种用途:
1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
2) 在网络上传送对象的字节序列。
序列化API
java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。只有实现了Serializable和Externalizable接口的类的对象才能被序列化。
java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
代码示例
public class MyClass {
public static void main(String[] argc) throws Exception{
//序列化对象
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("/Users/chenzongwen/Desktop/owen.obj"));
Student customer = new Student("Owen", 24);
out.writeObject("Hello Word"); //写入字面值常量
out.writeObject(new Date()); //写入匿名Date对象
out.writeObject(customer); //写入customer对象
out.close();
//反序列化对象
ObjectInputStream in = new ObjectInputStream(new FileInputStream("/Users/chenzongwen/Desktop/owen.obj"));
System.out.println("obj1 " + (String) in.readObject()); //读取字面值常量
System.out.println("obj2 " + (Date) in.readObject()); //读取匿名Date对象
Student obj3 = (Student) in.readObject(); //读取customer对象
System.out.println("obj3 " + obj3);
in.close();
}
static class Student implements Serializable {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return "name=" + name + ", age=" + age;
}
}
}
Android Serializable Or Parcelable
进行Android开发的时候,我们都知道不能将对象的引用传给Activities或者Fragments,我们需要将这些对象放到一个Intent或者Bundle里面,然后再传递。通过Android的API,我们知道有两种选择,即在传递对象时,需要对我们的对象进行 Parcelable 或者Serializable化。
怎么实现Parcelable接口?
从parcelable接口定义中,我们可以看到,实现parcelable接口,需要我们实现下面几个方法:
1、describeContents方法。内容接口描述,默认返回0就可以;
2、writeToParcel 方法。该方法将类的数据写入外部提供的Parcel中.即打包需要传递的数据到Parcel容器保存,以便从parcel容器获取数据,该方法声明如下:
writeToParcel (Parcel dest, int flags) 具体参数含义见javadoc
3、静态的Parcelable.Creator接口,本接口有两个方法:
createFromParcel(Parcel in) 从Parcel容器中读取传递数据值,封装成Parcelable对象返回逻辑层。
newArray(int size) 创建一个类型为T,长度为size的数组,仅一句话(return new T[size])即可。方法是供外部类反序列化本类数组使用。
parcelable接口的实现
public class Person implements Parcelable{
// 成员变量
private int id;
private String name;
// 1.必须实现Parcelable.Creator接口,否则在获取Person数据的时候,会报错,如下:
// android.os.BadParcelableException:
// Parcelable protocol requires a Parcelable.Creator object called CREATOR on class com.um.demo.Person
// 2.这个接口实现了从Percel容器读取Person数据,并返回Person对象给逻辑层使用
// 3.实现Parcelable.Creator接口对象名必须为CREATOR,不如同样会报错上面所提到的错;
// 4.在读取Parcel容器里的数据事,必须按成员变量声明的顺序读取数据,不然会出现获取数据出错
// 5.反序列化对象
public static final Parcelable.Creator<Person> CREATOR = new Creator(){
@Override
public Person createFromParcel(Parcel source) {
// TODO Auto-generated method stub
// 必须按成员变量声明的顺序读取数据,不然会出现获取数据出错
Person p = new Person();
p.setId(source.readInt());
p.setName(source.readString());
return p;
}
@Override
public Person[] newArray(int size) {
// TODO Auto-generated method stub
return new Person[size];
}
};
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
// TODO Auto-generated method stub
// 1.必须按成员变量声明的顺序封装数据,不然会出现获取数据出错
// 2.序列化对象
dest.writeInt(id);
dest.writeString(name);
}
}
总结:
使用Parcelable,3个理由:
1、Parcelable是Android 框架提供给我使用的,Google提供了比较好的接口和文档支持,例如上面的putExtra,就有对Parcelable数组的重载方法。
2、Parcelable效率更高,Parcelable底层实现是内存的copy,速度很快,Serializable是IO操作,而且会用到反射,相对比较慢,国外有人测试过,Parcelable比Serializable从序列化到传输到反序列化,平均要快10倍左右。
3、最后一个原因也是最重要的原因,Parcelable要序列化哪些字段,我们完全可以控制,而且还可以在其中加入各种转换,修饰,因为写接口暴露给我们了,我们可以自由定制,而Serializable就显的比较笨拙,而且需要一些额外的字节来存储类的信息,当然Serializable使用起来要更简单。
使用Serializable理由:
Parcelable只能在内存中处理,不能讲数据存储序列化到磁盘上或网络传输,尽管Serializable效率低,这种情况还得用Serializable。