Java序列化
Java序列化将对象转化成二进制字节数组,可以将二进制数据保存到磁盘或者进行网络传输,实现了对对象状态的保存,并且可通过反序列化来获取完全相同的对象副本,达到对象持久化的目的。
序列化的要求:
1. 序列化对象必须实现java.io.Serializable接口。
2. 序列化类中需添加serialVersionUID。如:private static final long serialVersionUID = 1L;
3. 若序列化对象中的成员变量是对象,则其也要实现java.io.Serializable接口。
注意点:
1. 类的对象序列化后,类中的serialVersionUID不可修改(修改会导致反序列化会失败)。
2. 类的对象序列化后,类中的变量有增删不会影响序列化,只是值会丢失。
3. 若父类序列化了,子类会继承父类的序列化,子类无需再显式实现java.io.Serializable接口。
4. 若父类没有序列化,子类序列化了,子类中的成员变量能正常序列化,但父类的中的成员变量不能序列化,会丢失。
5. 若成员变量不需序列化(敏感类成员变量,如密码之类的),加上transient关键字修饰,不参与序列化过程(transient一般只用于这种场景)。
6. 静态变量属于类级别的,不属于对象,属于类,不能被序列化。
7.常见的Date、String类已经实现了序列化,因此可以直接持久化。
另一种序列化方式是实现Exteranlizable接口。需要重写writeExternal和readExternal方法(这两个方法中若添加了transient字段的读取、写入,那么transient就失效了),它的效率比Serializable高一些,并且可以决定哪些成员变量需要序列化,但是对大量对象,或者重复对象,则效率低。
打开看Serializable接口源码,发现只是个空接口:
packagejava.io;public interfaceSerializable {
}
关于serialVersionUID
它在反序列化期间用于验证序列化对象的发送方和接收方是否已为该对象加载了与序列化兼容的类。JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化;否则反序列化将导致InvalidClassException异常。如果可序列化类没有显式声明serialVersionUID,则序列化运行时将根据该类计算该类的默认serialVersionUID值。如果原本的类新增了属性(相当于变成了另外一个类),但是serialVersionUID不做修改,可以反序列化成功,只不过新增的属性是零值。
实例:
1 public class User implementsSerializable{2
3 private static final long serialVersionUID = 1L;4
5 privateString name;6
7 private transientString pwd;8
9 //setter/getter/toString省略
10
11 }
1 //存储对象到文件
2
3 User user = new User("jzx","123");4
5 FileOutputStream fos = null;6
7 ObjectOutputStream oos = null;8
9 try{10
11 fos = new FileOutputStream("C:/Users/JZX/Desktop/demo.txt");12
13 oos = newObjectOutputStream(fos);14
15 oos.writeObject(user);16
17 } catch(IOException e) {18
19 e.printStackTrace();20
21 } finally{22
23 if(fos != null){24
25 fos.close();26
27 }28
29 if(oos != null){30
31 oos.flush();32
33 oos.close();34
35 }36
37 }
1 //读取序列化存储的对象
2
3 FileInputStream fis = null;4
5 ObjectInputStream ois = null;6
7 try{8
9 fis = new FileInputStream("C:/Users/JZX/Desktop/demo.txt");10
11 ois = newObjectInputStream(fis);12
13 System.out.println(ois.readObject());14
15 } catch(IOException e) {16
17 e.printStackTrace();18
19 } finally{20
21 if(fis != null){22
23 fis.close();24
25 }26
27 if(ois != null){28
29 ois.close();30
31 }32
33 }
输出:
User{name='jzx', pwd='null'}
可见加了transient 的pwd并没有被序列化存储。
如果序列化类中存在final变量,该属性只会被赋值一次,序列化存储以后,对原先类的该变量值进行修改,那么反序列化时将会重新计算(用新值)。而如果final变量是通过构造器赋值的,那么反序列化后将不会用新值(反序列化时构造器不会被调用)。
也可借助org.apache.commons.lang3.SerializationUtils实现对象的序列化及反序列化:
1 byte[] bytes =SerializationUtils.serialize (user);2
3 User user =SerializationUtils.deserialize (bytes);4
5 System.out.println(user);