java 提供Serializable解决对象可持久化的问题,它为分布式或者异构的环境下java对象的传输提供了先决支持条件。在序列化和反序列化的时候,如果在server/client 端 传输对象的类型版本变更 可能导致无法反序列化生成所需要的对象。如Person类只有name属性 在序列化后,如果Person类此时添加另一个属性 age 这个时候反序列化就会报错InvalidClassException 异常。原因是序列化和反序列化所对应的类型版本不一致,而java如何判断他们的版本不一致呢,她是根据SerialVersionUID 流标识符判断的,在实现Serializable接口的时候 我们应该显式的声明uid 如果没显式声明,jvm在编译的时候会隐式计算出这个uid 所以上述问题在版本更替的时候,如果在Person 中显示声明的话,向jvm“撒谎”认为版本一致就可以避免InvalidClassException 异常的发生,并顺利转换生成所需要的对象,只是反序列化的对象没有新增的功能。但是开发过程中尽量保证两端类型完整一致。
先提供测试类
person
package test;
import java.io.Serializable;
/**
*
* @see
* @author Hu
* @date2015-3-5 下午2:47:33
* @version
* @desc TODO
*/
public class Person implements Serializable{
/**
* uid
*/
private static final long serialVersionUID = 6043921487754688987L;
private String normal ="normal1";
private transient String transientStr= "transientStr1";
private final String finalStr="finalStr1";
@Override
public String toString() {
return "Person [normal=" + normal + ", transientStr=" + transientStr + ", finalStr=" + finalStr + "]";
}
}
SerializableUtils
package test;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
*
* @see
* @author Hu
* @date 2015-3-5 下午2:51:44
* @version
* @desc TODO
*/
public class SerializableUtils {
private final static String fp = "d:/SerializableUtils.txt";
/**
* 序列化服务
* @param o
*/
public static void serializeObj(Serializable o) {
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fp));
oos.writeObject(o);
oos.close();
} catch (Exception e) {
throw new RuntimeException("反序列化失败");
}
}
/**
* 反序列化服务
* @return
*/
public static Object deserializeObj() {
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream(fp));
return ois.readObject();
} catch (Exception e) {
throw new RuntimeException("");
} finally{
if(ois!=null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
1.当调用serializeObj(new Person())服务的时候 person对象被以流的形式保存的文件中,在反序列化调用deserializeObj
public static void main(String[] args) {
Person person = new Person();
SerializableUtils.serializeObj(person);
Object obj = SerializableUtils.deserializeObj();
System.out.println(obj);
}
生成的结果如下:
Person [normal=normal1, transientStr=null, finalStr=finalStr1]
总结:修饰符transient修饰的属性不会被写入留中,所以反序列化的时候 默认值null初始化对象
2.修改Person初始化的值如下
package test;
import java.io.Serializable;
/**
*
* @see
* @author Hu
* @date2015-3-5 下午2:47:33
* @version
* @desc TODO
*/
public class Person implements Serializable{
/**
* uid
*/
private static final long serialVersionUID = 6043921487754688987L;
private String normal ="normal2";
private transient String transientStr= "transientStr2";
private final String finalStr="finalStr2";
@Override
public String toString() {
return "Person [normal=" + normal + ", transientStr=" + transientStr + ", finalStr=" + finalStr + "]";
}
}
测试代码
public static void main(String[] args) {
Person person = new Person();
Object obj = SerializableUtils.deserializeObj();
System.out.println(obj);
}
结果:
Person [normal=normal1, transientStr=null, finalStr=finalStr2]
总结: 反序列化过程中final常量从新计算生成最新的常量
3.构造函数和方法初始化变量(将属性赋值在构造函数里面初始化)
package test;
import java.io.Serializable;
/**
*
* @see
* @author Hu
* @date 2015-3-5 下午2:47:33
* @version
* @desc TODO
*/
public class Person implements Serializable {
/**
* uid
*/
private static final long serialVersionUID = 6043921487754688987L;
private String normal;
private transient String transientStr;
private final String finalStr;
public Person() {
normal = "normal3";
transientStr = "transientStr3";
finalStr = "finalStr3";
}
@Override
public String toString() {
return "Person [normal=" + normal + ", transientStr=" + transientStr + ", finalStr=" + finalStr + "]";
}
}
测试代码:
public static void main(String[] args) {
Person person = new Person();
Object obj = SerializableUtils.deserializeObj();
System.out.println(obj);
}
结果:
Person [normal=normal1, transientStr=null, finalStr=finalStr1]
总结:从2中知道final在反序列化的时候需要重新计算生成值,但是这里反序列化的时候却仍然是第一次序列化的时候的值,这是因为jvm在反序列化的时候不执行构造函数和初始化函数,所以值都是最原始的初始值,而final虽然从新计算但是jvm发现finalStr未被初始化,jvm就直接将序列化中的初始值返回给生成的对象。