虽然从事开发工作两年,最突出的提升也就是应付工作的能力,对于基础概念还是不知所云。惭愧惭愧。。。
1、序列化的存在一定有它的道理,先来看看序列化的前世今生:
Java平台允许我们在内存中创建可复用的Java对象,在JVM处于运行时,这些对象才可能存在,也就是说这些对象的生命周期不会比JVM的生命周期长。但在实际应用里,可能要求在JVM停止运行后能够保持(持久化)指定的对象,以便在将来重新读取被保持的对象,那么问题来了。Java对象序列化应运而生。
2、什么是java序列化?
java序列化是一个把java对象转换为字节序列的过程,也就是把内存中Java对象的状态转换成跟平台无关的二进制流,从而允许将这种二进制流保存在磁盘上、网络传输等。必须注意的是,对象序列化保存的是对象的状态,即它的成员变。因此,对象序列化不会关注类中的静态变量。反序列化就是从二进制流(序列)转化为对象的过程。
3、序列化原理
- 每个序列化对象都是采用一个序列号进行保存;
- 当序列化一个对象是,程序将检查改对象是否已经序列化过:
——该对象如果没进行序列化,则采用六中数据来构建它,并为该对象关联一个序列号;
——该对象已经序列或,则程序直接输出该对象关联的序列号,通过该序列号获取该对象的引用。
3、如何实现对象序列化?
如果要让一个对象支持序列化机制,必须让它的类是可序列化的,该类必须实现一个可序列化的接口。
- Serializable
- Extmalizable
这里有几个原则,相信有开发经验的同学都能体会到下面的提到的几点:
- Serializable是一个标示性接口,接口中没定义任何方法或字段,仅用于表示可序列化的语义;
- 静态变量和成员方法不可序列化;
- 一个类能被序列化,该类中的所有引用对象也必须是可以被序列化的。否则整个序列化操作会失败,并且抛出一个NotSerializableException,除非我们将不可序列化的引用标记为transient。
Serializable接口
Serializable接口是在java序列化中最常用的,只要实现Serializable接口即可。
当实体类实现这个接口的时候,eclipse或者ide应该会给一个警告,提示你serialVersionUID这个成员变量,androidstudio自动生成serialVersionUID可参考这篇文章:链接
透过现象问问题,什么是serialVersionUID?
1、serialVersionUID可以理解为类的版本号,只有版本号相同,反序列化才能成功。如果不主动导入serialVersionUID,那么就会生成一个默认的。
2、serialVersionUID是通过你的类定义信息计算所得,也就是说,如果这个类进行了修改,就会产生一个不同的serialVersionUID,那么之前序列化的字节流就不能正常反序列化了。
3、但是,serialVersionUID知识一个版本号而已,在某些情况下,我们对实体类进行了修改,比如增加或删除一个成员变量,只要这时候不修改serialVersionUID,那么之前序列化好的字节流仍可以正常被反序列化成新的对象。
public class Test implements Serializable {
private static final long serialVersionUID = -1410115609728301016L;
}
自定义序列化
在实际开发中,可能会有这样的需求,一个实体类中,某些属性需要序列化,某些属性不需要。这样可以有效减少序列化的长度,提高效率。
先看看前面提到transient关键字:
public class Test implements Serializable {
private static final long serialVersionUID = -1410115609728301016L;
// 反序列化后name的value将为null
transient String name;
String age;
}
上面的例子同时也说明:使用tranisent修饰的变量,在经过序列化和反序列化之后,Java对象会丢失该实例的变量值,但是变量名仍参加序列化。
使用transient关键字只是隔离了变量值,有没有什么方法直接不序列化实例变量。答案是肯定的,java的自定义序列化机制可以做到。
1、实现自定义序列化(一)
在序列化和反序列化过程中需要特殊处理的类应该提供如下方法,用于实现自定义序列化:
- writeObject()
- readObject()
这两个方法并不属于任何类和接口,只要在要序列化的类中提供这两个方法,就会在序列化机制中自动被调用。
其中wirteObject方法用于写入特定类的实例状态,相应的readObject方法可以恢复它。通过重写该方法,开发者可以获取对序列化的控制。
public class Test implements Serializable {
private static final long serialVersionUID = -1410115609728301016L;
String name;
String age;
//JAVA BEAN自定义的writeObject方法
private void writeObject(ObjectOutputStream out) throws IOException {
System.out.println("writeObejct ------");
out.writeObject(new StringBuffer(name).reverse());
out.writeObject(new StringBuffer(age).reverse());
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
System.out.println("readObject ------");
this.name = ((StringBuffer) in.readObject()).reverse().toString();
this.age = ((StringBuffer) in.readObject()).reverse().toString();
}
}
2、实现自定义序列化(二)
Externalizable接口
1、这是另一种自定义序列化机制,可以完全有开发者决定存储和恢复对象数据。
2、用法和Serializable接口非常相似,只是Externalizable接口强制开发自定义序列化。
3、通过writeExternal(…)和writeExternal(…)实现序列化和反序列化、
4、注意:实现Externalizable接口的类必须提供一个无参构造函数,因为在反序列化对象是,对象流将调用无参构造函数创建一个对象,在调用readExternal(…)方法。
public class Test implements Externalizable {
private static final long serialVersionUID = -1410115609728301016L;
String age;
// 必备的无参构造函数
public Test() {
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
}
}
2、实现自定义序列化(三)
写入时替换对象——writeReplace()
writeReplace()是一种彻底的自定义机制,实现writeReplace()方法后,序列化时会先调用writePeplace方法将当前对象替换成另一个对象,该方法会返回替换后的对象并将其写入流中。
public class Test implements Serializable {
private static final long serialVersionUID = -1410115609728301016L;
transient String name;
String age;
private Object writeReplace() throws ObjectStreamException {
ArrayList<Object> list = new ArrayList<>();
list.add(name);
list.add(age);
return list;
}
}
补充:
对于某些单例对象,可以在类定义的时候加上readResolve()方法,返回单例的实体类,这样在同一内存空间反序列化出来,会调用readResolve()方法获得原单例对象的引用,避免因为序列化和反序列化破坏了单例。
public class MySingleton {
private MySingleton() {
}
private static final MySingleton INSTANCE = new MySingleton();
public static MySingleton getInstance() {
return INSTANCE;
}
private Object readResolve() throws ObjectStreamException {
return INSTANCE;
}
}
android的Parcelable接口
Parcelable是android为我们提供的序列化接口,使用比Serializable复杂点,但是效率很高,号称比Serializable快10倍。
安卓开发里,Parcelable和Serializable都是实现序列化并且可用于Intent间传递数据,Serializable是Java的实现方式,可能会频繁的IO操作,所以消耗比较大,但是实现方式简单 。
Parcelable是Android提供的方式,效率比较高,但是实现起来复杂一些 ,现在as有插件,用起来一点也不复杂。
二者的选取规则是:倾向于性能,当然选择Parcelable;倾向于快简省,选择Serializable。
参考资料:
https://www.cnblogs.com/dongguacai/p/5721056.html
https://www.jianshu.com/p/7f36f22c1a64
https://www.jianshu.com/p/7ad3d20b3067
http://blog.youkuaiyun.com/justin_1107/article/details/72903006