1.概念
(1)序列化
概念:将数据结构或对象转换成二进制串的过程。
(2)反序列化
概念:将在序列化生成的二进制串转换成数据结构或对象的过程。
(3)持久化
概念:把数据结构或对象存储起来(文件,硬盘等)。
2.实现
代码
public class Demo {
public static void main(String[] args) throws Exception {
//Serializable
String fileName = "/Users/macbook/Desktop/aa/user.txt";
User user = new User();
user.setName("哈哈");
user.setAge(100);
writeObject(user, fileName);
User userBack = (User) readObject(fileName);
System.out.println("read serializable user:name=" + userBack.getName() + ";age:" + userBack.getAge());
//read serializable user:name=哈哈;age:0
//Externalizable
String fileName2 = "/Users/macbook/Desktop/aa/user2.txt";
User2 user2 = new User2();
user2.setName("哈哈2");
user2.setAge(200);
writeObject(user2, fileName2);
User2 userBack2 = (User2) readObject(fileName2);
System.out.println("read serializable user:name=" + userBack2.getName() + ";age:" + userBack2.getAge());
//read serializable user:name=哈哈2;age:200
}
// 把对象序列化到文件
private static void writeObject(Object object, String fileName) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName));
oos.writeObject(object);
oos.close();
}
// 反序列化到内存
private static Object readObject(String fileName) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName));
Object object = ois.readObject();
ois.close();
return object;
}
}
2.1 Serializable
public class User implements Serializable {
//不反序列化的话,一般不需要写serialVersionUID
private static final long serialVersionUID = -1188549698363555384L;
private String name;
private transient int 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;
}
}
serialVersionUID
Android自动生成serialVersionUID方法链接
作用:用于检测序列化操作与反序列化操作是否一致,如果一致就说明序列化类的版本与当前类版本是一样的,可以反序列化成功,否则失败。
报错如下:
Exception in thread "main" java.io.InvalidClassException: com.example.demo.study.xlh.User; local class incompatible: stream classdesc serialVersionUID = -1188549698363555384, local class serialVersionUID = -1188549698363555386
transient
作用:修饰不会被序列化的属性
注意事项:
-
静态变量不能被序列化。
-
Serializable序列化后增加或者减少字段,当不指定serialVersionUID时反序列化失败。
-
一个对象要进行序列化,如果该对象成员变量是引用类型的,那这个引用类型也一定要是可序列化的,否则报错:NotSerializableException。
-
如果类是可序列化的,但是其父类是不可序列化的,反序列化后不能拿到父类的属性值。
-
父类实现了Serializable接口,子类继承了父类,没有实现Serializable接口,那么子类同样也是可被序列化的。
2.2Externalizable
public class User2 implements Externalizable {
public User2() {
}
public User2(String name, int age) {
this.name = name;
this.age = age;
}
private String name;
private transient int 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 void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(name);
out.writeObject(age);
}
@Override
public void readExternal(ObjectInput in) throws ClassNotFoundException, IOException {
name = (String) in.readObject();
age = (int) in.readObject();
}
}
Externalizable接口扩展自java.io.Serializable接口。实现java.io.Serializable即可获得对类的对象的序列化功能。而Externalizable可以通过writeExternal()和readExternal()方法可以指定序列化哪些属性。
注意事项:
-
Externalizable会调用默认的构造器,没有默认构造器则报错:Exception in thread "main" java.io.InvalidClassException: com.example.demo.study.xlh.User2; no valid constructor。Serializable不会调用默认构造器。
-
继承Externalizable接口时,transient无效,属性是否要序列化,主要通过writeExternal()和readExternal()方法。
-
Externalizable的writeExternal写入顺序要和readExternal读取顺序一致,否则报错:
@Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(name); out.writeObject(age); } @Override public void readExternal(ObjectInput in) throws ClassNotFoundException, IOException { age = (int) in.readObject(); name = (String) in.readObject(); } //Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
2.3Parcelable
public class User3 implements Parcelable {
private String name;
private int age;
private transient int sex;
protected User3(Parcel in) {
name = in.readString();
age = in.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<User3> CREATOR = new Creator<User3>() {
@Override
public User3 createFromParcel(Parcel in) {
return new User3(in);
}
@Override
public User3[] newArray(int size) {
return new User3[size];
}
};
}
Parcel提供了一套机制,可以将序列化之后的数据写入到一个共享内存中,其他进程通过Parcel可以从这块共享内存中读出字节流,并反序列化成对象。
3.对比
Serializable | parcelable |
---|---|
通过io对硬盘操作,速度较慢 | 直接内存操作,效率高,性能好 |
大小不受限制 | 一般不超过1M,修改内核也只能4M |
大量使用反射,产生内存碎片 | |
存储设备或者网络传输 | 应用程序在内存间数据传输 |
参考资料:Android序列化总结 - 简书