java序列化和反序列化
Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应
实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。
序列化: 将数据结构或对象转换成二进制串的过程。
反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。
只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自Serializable接口,
transient的用法
在实际开发过程中,我们常常会遇到这样的问题,这个类的有些属性需要序列化,而其他属性不需要被序列化,打个比方,如果一个用户有一些敏感信息
(如密码,银行卡号等),为了安全起见,不希望在网络操作(主要涉及到序列化操作,本地序列化缓存也适用)中被传输,这些信息对应的变量就可以
加上transient关键字
使用transient的字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。
核心代码:
1 private static final long serialVersionUID = 8294180014912103005L; 2 3 private String username; 4 private transient String passwd; 5 6 User user = new User(); 7 user.setUsername("Tom"); 8 user.setPasswd("123"); 9 10 11 ObjectOutputStream os = new ObjectOutputStream( 12 new FileOutputStream("C:/user.txt")); 13 os.writeObject(user); // 将User对象写进文件 14 os.flush(); 15 os.close(); 16 17 ObjectInputStream is = new ObjectInputStream(new FileInputStream( 18 "C:/user.txt")); 19 user = (User) is.readObject(); // 从流中读取User的数据 20 is.close();
注意:
1)一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。
2)transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类
需要实现Serializable接口。
3)被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。
第三点可能有些人很迷惑,因为发现在User类中的username字段前加上static关键字后,程序运行结果依然不变
实际上是这样的:第三点确实没错(一个静态变量不管是否被transient修饰,均不能被序列化),反序列化后类中static型变量username的值为当前
JVM中对应static变量的值,这个值是JVM中的不是反序列化得出的
1 private static final long serialVersionUID = 8294180014912103005L; 2 3 public static String username; 4 private transient String passwd; 5 6 User user = new User(); 7 user.setUsername("Tom"); 8 user.setPasswd("123"); 9 10 ObjectOutputStream os = new ObjectOutputStream( 11 new FileOutputStream("C:/user.txt")); 12 os.writeObject(user); // 将User对象写进文件 13 os.flush(); 14 os.close(); 15 16 // 在反序列化之前改变username的值 17 User.username = "jack"; 18 19 ObjectInputStream is = new ObjectInputStream(new FileInputStream( 20 "C:/user.txt")); 21 user = (User) is.readObject(); // 从流中读取User的数据 22 is.close();
什么情况下需要序列化:
- 当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
- 当你想用套接字在网络上传送对象的时候;
- 当你想通过RMI传输对象的时候;
在Java中socket传输数据时,数据类型往往比较难选择。可能要考虑带宽、跨语言、版本的兼容等问题。
比较常见的做法有两种:一是把对象包装成JSON字符串传输,
二是采用java对象的序列化和反序列化。
随着Google工具protoBuf的开源,protobuf也是个不错的选择。对JSON,Object Serialize,ProtoBuf 做个对比。
相关注意事项:
a)序列化时,只对对象的状态进行保存,而不管对象的方法;
b)当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;
c)当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;