Serializable的作用: 是java提供的一个序列化接口,用来为对象提供标准的序列化和反序列化操作。使用Serializable做序列化比较简单。
使用方式:
- 直接实现Serializable接口
- 声明一个静态值serialVersionUID。这个值并不是必须的,但是最好手动序列化。因为不设置这个值java会自动根据当前对象的hash生成一个值,但是这样容易引发一些问题,比如反序列化时程序崩溃。我们可以从后面的例子中看到。
注意: 1、序列化的时候会将Serializable的值一起写入文件,然后反序列化的时候再利用文件中的Serializable值和当前类的Serializable进行对比,如果不一致则会出现反序列化失败的问题,会直接程序崩溃,后面会举例。 2、静态变量是属于类不属于对象,所以不参与序列化过程。
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String mUserName;
private int mUserAge;
public User(String userName, int userAge) {
this.mUserName = userName;
this.mUserAge = userAge;
}
public String getUserName() {
return mUserName;
}
public int getUserAge() {
return mUserAge;
}
}
- 如果不手动设置serialVersionUID 会发生什么?
public class User implements Serializable {
private String mUserName;
private int mUserAge;
public User(String userName, int userAge) {
this.mUserName = userName;
this.mUserAge = userAge;
}
public String getUserName() {
return mUserName;
}
public int getUserAge() {
return mUserAge;
}
}
public class Client {
public static void main(String[] args) {
User user = new User("Kyle", 18);
//先序列化到一个文件中
try {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cache.txt"));
out.writeObject(user);
out.close();
} catch (IOException e) {
e.printStackTrace();
}
// try {
// ObjectInputStream in = new ObjectInputStream(new FileInputStream("cache.txt"));
// User userNew = (User) in.readObject();
// in.close();
// System.out.println("反序列化结果:" + userNew.getUserName() + userNew.getUserAge());
// } catch (IOException e) {
// e.printStackTrace();
// } catch (ClassNotFoundException e) {
// e.printStackTrace();
// }
}
}
public class Client {
public static void main(String[] args) {
// User user = new User("Kyle", 18);
// try {
// ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cache.txt"));
// out.writeObject(user);
// out.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
//然后反序列化
try {
ObjectInputStream in = new ObjectInputStream(new FileInputStream("cache.txt"));
User userNew = (User) in.readObject();
in.close();
System.out.println("反序列化结果:" + userNew.getUserName() + userNew.getUserAge());
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出结果:
但是当后面我们在User类中增加一个字段时:由于此时在反序化时User类自动生成的Serializable已经自动改变,所以反序化就会失败,抛出JE。所以我们应该手动设置Serializable,一边可以最大化的反序化原始数据。
public class User implements Serializable {
private String mUserName;
private int mUserAge;
private boolean mIsMale;
public User(String userName, int userAge) {
this.mUserName = userName;
this.mUserAge = userAge;
}
public String getUserName() {
return mUserName;
}
public int getUserAge() {
return mUserAge;
}
}
我们也可以在实验一下设置serialVersionUID的情况,即使我们新增加字段,还是可以反序列化出数据。
Parcelable的作用: Parcelable也是一个接口,是Android中提供的序列化和反序列化的方式,由于Serializable在开销较大,所以产生了Parcelable这种方式,可以方便在内存中进行序列化和数据传输。
使用:
public class User implements Parcelable{
public int userId;
public String userName;
public boolean isMale;
public Book book;
public User(int userId, String userName, boolean isMale) {
this.userId = userId;
this.userName = userName;
this.isMale = isMale;
}
protected User(Parcel in) {
userId = in.readInt();
userName = in.readString();
isMale = in.readByte() != 0;
book = in.readParcelable(Thread.currentThread().getContextClassLoader());
}
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];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(userId);
dest.writeString(userName);
dest.writeByte((byte) (isMale ? 1 : 0));
dest.writeParcelable(book, 0);
}
}
Parcelablede 方法说明:
方法 | 功能 |
---|---|
User(Parcel in) | 从序列化后的对象中创建原始对象 |
createFromParcel(Parcel in) | 从序列化后的对象中创建原始对象 |
newArray(int size) | 创建指定长度的原始对象数组 |
describeContents() | 返回当前对象的内容描述。如果含有文件描述符返回1, 否则返回0, 大部分情况都返回0 |
writeToParcel(Parcel dest, int flags) | 将当前对象写入序列化结构中,其中flags标识有两种值:0或者1.为1时标识当前对象需要作为返回值返回,不能立即释放资源,几乎所有情况都为0 |
Serializable与Parcelable的区别:
- 实现差异:Serializable实现起来很简单,Parcelable实现起来麻烦,需要实现一些方法
- 效率方面:Serializable效率较低,内存开销大,故经常用于持久化数据的序列化,如存储文件,网络传输等。而在内存间传输数据则应该使用Parcelable,效率高很多,如Android中的AIDL
Serializable与Parcelable的共同点:
- 无论是Parcelable还是Serializable,执行反序列操作后的对象都是新创建的,与原来的对象并不相同,只不过内容一样罢了。