所谓序列化,简单一点理解,就是将对象转换成字节数组,反序列化是将字节数组恢复为对象。凡是要在网络上传输的对象、要写入文件的对象、要保存到数据库中的对象都要进行序列化。Java对象是无法直接保存到文件中,或是存入数据库中的。如果要保存到文件中,或是存入数据库中,就要将对象序列化,即转换为字节数组才能保存到文件中或是数据库中。文件或者数据库中的字节数组拿出来之后要转换为对象才能被我们识别,即反序列化。
java中要序列化比较简单,只要让该类实现java.io.Serializable接口即可。实现该接口之后,应该在该类中声明如下常量:
private static final long serialVersionUID = -596668932026414547L;
字面意思上是序列化的版本号,凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态常量。常量的值可以随意给,通常情况由MyEclipse根据类名、方法和属性名去生成即可。该值一旦给定之后尽量不要修改,因为序列化之后,如果该值被修改的话,反序列化就会出现类似与下边的异常:
java.io.InvalidClassException: 类名; local class incompatible: stream classdesc serialVersionUID = -596668932026414547, local class serialVersionUID = 59
无法进行序列化。-596668932026414547为原来的值,59为改过之后的值。不声明serialVersionUID常量编辑器会警告,不会报错。这时会根据字段和特定的算法生成一个serialVersionUID,当属性有变化时这个id值就会发生变化,所以反序列化的时候就会失败。
一个类实现 了Serializable接口,那么它的所有子类都间接实现了此接口,所以它可以被序列化。如果父类没有实现Serializable接口,但其子类实现了此接口,那么这个子类是可以序列化的,但是在反序列化的过程中会调用 父类的无参构造函数,所以在其直接父类(注意是直接父类)中必须有一个无参的构造函数。通常情况下,我们的做法是基类实现Serializable接口,其它的DTO、VO、POJO类继承自这个基类。
通过下边的两段代码可将对象转换为字节数组,即序列化。或者将字节数组转换为对象,即反序列化。
public static byte[] objectToBytes(Object object) throws IOException {
ByteArrayOutputStream baos = null;
ObjectOutputStream oos = null;
byte[] bytes = null;
try {
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(object);
bytes = baos.toByteArray();
} catch (IOException e) {
logger.error("将对象转化为字节数组出现异常", e);
} finally {
if (oos != null) {
oos.close();
}
if (baos != null) {
baos.close();
}
}
return bytes;
}
@SuppressWarnings("unchecked")
public static T bytesToObject(byte[] bytes, Class c) throws IOException, ClassNotFoundException {
Object object = null;
ByteArrayInputStream bais = null;
ObjectInputStream ois = null;
try {
bais = new ByteArrayInputStream(bytes);
ois = new ObjectInputStream(bais);
object = ois.readObject();
} catch (Exception e) {
logger.error("将字节数组转化为对象出现异常", e);
} finally {
if (ois != null) {
ois.close();
}
if (bais != null) {
bais.close();
}
}
if (object != null) {
return (T) object;
}
return null;
}