Java 序列化简介
Java对象序列化是JDK 1.1中引入的一组开创性特性之一,是Java语言内建的一种对象持久化方式,用于作为一种将Java对象的状态转换为字节数组,以便存储或传输的机制,以后仍可以将字节数组转换回Java对象原有的状态。
实际上,序列化的思想是冻结对象状态,传输对象状态,如写到磁盘或者通过网络使用RMI远程方法调用等,然后解冻对象状态,重新获得可用的Java对象。
相关接口及类
Java为了方便开发人员将Java对象进行序列化及反序列化提供了一套方便的API来支持,其中包括以下接口和类:
- java.io.Serializable
- java.io.Externalizable
- java.io.ObjectOutput
- java.io.ObjectInput
- java.io.ObjectOutputStream
- java.io.ObjectInputStream
- javax.crypto.SealedObject
- java.security.SignedObject
如何对Java对象进行序列化与反序列化
在Java中,只要一个类实现了java.io.Serializable接口,那么它就可以被序列化,这个接口属于标记接口,源码如下:
/* * @see java.io.ObjectOutputStream * @see java.io.ObjectInputStream * @see java.io.ObjectOutput * @see java.io.ObjectInput * @see java.io.Externalizable * @since JDK1.1 */public interface Serializable {} |
当对象没有实现java.io.Serializable接口时
如下所示的SerializableObject类在没在实现java.io.Serializable接口时,运行下面SerializeDeserializeExample.main方法:
public class SerializableObject { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString() { return "SerializableObject{" + "name='" + name + '\'' + '}'; }} |
SerializeDeserializeExample.java
public class SerializeDeserializeExample { private static final Logger LOGGER = LoggerFactory.getLogger(SerializeDeserializeExample.class); public static void serialize(SerializableObject serializableObject) throws FileNotFoundException, IOException { File file = new File(System.getProperty("java.io.tmpdir") + "serializableObject.ser"); ObjectOutputStream objectOutputStream = null; try { objectOutputStream = new ObjectOutputStream(new FileOutputStream(file)); objectOutputStream.writeObject(serializableObject); objectOutputStream.flush(); } finally { if (objectOutputStream != null) { objectOutputStream.close(); } } } public static SerializableObject deSerialize() throws FileNotFoundException, IOException, ClassNotFoundException { ObjectInputStream objectInputStream = null; try { File file = new File(System.getProperty("java.io.tmpdir") + "serializableObject.ser"); objectInputStream = new ObjectInputStream(new FileInputStream(file)); Object object = objectInputStream.readObject(); SerializableObject deserializedObject = (SerializableObject) object; LOGGER.info(deserializedObject .getName()); return deserializedObject; } finally { if (objectInputStream != null) { objectInputStream.close(); } } } public static void main(String[] args) throws FileNotFoundException, ClassNotFoundException, IOException { SerializableObject serializableObject = new SerializableObject(); serializableObject.setName("test java.io.Serializable"); serialize(serializableObject); SerializableObject deSerializedObject = deSerialize(); LOGGER.info("{}", deSerializedObject); }} |
程序会抛出如下异常NotSerializableException:
Exception in thread "main" java.io.NotSerializableException: com.example.test.io.SerializableObject
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at com.example.test.io.SerializeDeserializeExample.serialize(SerializeDeserializeExample.java:40)
at com.example.test.io.SerializeDeserializeExample.main(SerializeDeserializeExample.java:67)
SerializeDeserializeExample这个类中使用了对象序列化和反序列化操作时最重要的2个类:ObjectInputStream和ObjectOutputStream,其中ObjectInputStream类中最重要的方法为:
public final void writeObject(Object obj) throws IOException {} |
ObjectInputStream对应的方法为:
public final Object readObject() throws IOException, ClassNotFoundException {} |
SerializableObject实现java.io.Serializable接口
import java.io.Serializable;public class SerializableObject implements Serializable { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString() { return "SerializableObject{" + "name='" + name + '\'' + '}'; }} |
再次运行SerializeDeserializeExample.main方法后输出如下内容:
2016-07-27 21:41:12.266 - INFO --- [ main] c.e.test.io.SerializeDeserializeExample [ 56] : test java.io.Serializable
2016-07-27 21:41:12.267 - INFO --- [ main] c.e.test.io.SerializeDeserializeExample [ 73] : SerializableObject{name='test java.io.Serializable'}
SerializableObject对象被正确的序列化保存到文件,并从文件中重新加载为一个SerializableObject实例。
为SerializableObject声明serialVersionUID
上面SerializableObject虽然实现了java.io.Serializable接口,但并没有设置serialVersionUID,jvm会为当前类自动添加一个serialVersionUID。
Eclipse中会出现The serializable class SerializableObject does not declare a static final serialVersionUID field of type long这样的警告信息,java doc中建议实现java.io.Serializable接口的类添加一个固定的serialVersionUID,避免不同的JDK实现因为serialVersionUID算法不同导致反序列化出现问题。
serialVersionUID必须使用static和final修饰,并且为long型的任意一个数字,并且java doc中也明确提到尽可能的将它声明为private。
如果在上述SerializeDeserializeExample.main运行成功后,再为SerializableObject设置一个serialVersionUID,如下所示:
import java.io.Serializable;public class SerializableObject implements Serializable { private static final long serialVersionUID = 1L; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString() { return "SerializableObject{" + "name='" + name + '\'' + '}'; }} |
前面SerializeDeserializeExample.main方法运行生成的serializableObject.ser文件还在,执行下面的代码进行反序列化:
public class OnlyDeserializeExample { private static final Logger LOGGER = LoggerFactory.getLogger(OnlyDeserializeExample.class); public static SerializableObject deSerialize() throws FileNotFoundException, IOException, ClassNotFoundException { ObjectInputStream objectInputStream = null; try { File file = new File(System.getProperty("java.io.tmpdir") + "serializableObject.ser"); objectInputStream = new ObjectInputStream(new FileInputStream(file)); Object object = objectInputStream.readObject(); SerializableObject deserializedObject = (SerializableObject) object; LOGGER.info(deserializedObject .getName()); return deserializedObject; } finally { if (objectInputStream != null) { objectInputStream.close(); } } } public static void main(String[] args) throws FileNotFoundException, ClassNotFoundException, IOException { SerializableObject deSerializedObject = deSerialize(); LOGGER.info("{}", deSerializedObject); }} |
运行后会抛出InvalidClassException异常如下:
Exception in thread "main" java.io.InvalidClassException: com.example.test.io.SerializableObject; local class incompatible: stream classdesc serialVersionUID = 8024319097192047636, local class serialVersionUID = 1
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at com.example.test.io.OnlyDeserializeExample.deSerialize(OnlyDeserializeExample.java:20)
at com.example.test.io.OnlyDeserializeExample.main(OnlyDeserializeExample.java:32)
serialVersionUID就是用来保证对象序列化和反序列化时使用的是相同的class文件。
当SerializableObject中有其他对象引用时
以下UnSerializableReference类没有实现java.io.Serializable接口:
public class UnSerializableReference { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString() { return "UnSerializableReference{" + "name='" + name + '\'' + '}'; }} |
而SerializableObject引用了此对象,代码如下:
import java.io.Serializable;public class SerializableObject implements Serializable { private static final long serialVersionUID = 1L; private String name; private UnSerializableReference unSerializableReference; public String getName() { return name; } public void setName(String name) { this.name = name; } public UnSerializableReference getUnSerializableReference() { return unSerializableReference; } public void setUnSerializableReference(UnSerializableReference unSerializableReference) { this.unSerializableReference = unSerializableReference; } public String toString() { return "SerializableObject{" + "name='" + name + '\'' + ", unSerializableReference=" + unSerializableReference + '}'; }} |
运行以下SerializeDeserializeExample.main方法:
public class SerializeDeserializeExample { private static final Logger LOGGER = LoggerFactory.getLogger(SerializeDeserializeExample.class); public static void serialize(SerializableObject serializableObject) throws FileNotFoundException, IOException { File file = new File(System.getProperty("java.io.tmpdir") + "serializableObject.ser"); ObjectOutputStream objectOutputStream = null; try { objectOutputStream = new ObjectOutputStream(new FileOutputStream(file)); objectOutputStream.writeObject(serializableObject); objectOutputStream.flush(); } finally { if (objectOutputStream != null) { objectOutputStream.close(); } } } public static SerializableObject deSerialize() throws FileNotFoundException, IOException, ClassNotFoundException { ObjectInputStream objectInputStream = null; try { File file = new File(System.getProperty("java.io.tmpdir") + "serializableObject.ser"); objectInputStream = new ObjectInputStream(new FileInputStream(file)); Object object = objectInputStream.readObject(); SerializableObject deserializedObject = (SerializableObject) object; LOGGER.info(deserializedObject .getName()); return deserializedObject; } finally { if (objectInputStream != null) { objectInputStream.close(); } } } public static void main(String[] args) throws FileNotFoundException, ClassNotFoundException, IOException { SerializableObject serializableObject = new SerializableObject(); serializableObject.setName("test java.io.Serializable"); UnSerializableReference unSerializableReference = new UnSerializableReference(); unSerializableReference.setName("unSerializableReference"); serializableObject.setUnSerializableReference(unSerializableReference); serialize(serializableObject); SerializableObject deSerializedObject = deSerialize(); LOGGER.info("{}", deSerializedObject); }} |
运行抛出NotSerializableException异常如下:
Exception in thread "main" java.io.NotSerializableException: com.example.test.io.UnSerializableReference
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at com.example.test.io.SerializeDeserializeExample.serialize(SerializeDeserializeExample.java:40)
at com.example.test.io.SerializeDeserializeExample.main(SerializeDeserializeExample.java:71)
也就是说SerializableObject要正确的序列化,则要求其属性字段都是可序列化的,除非用transient修饰符明确指出当前属性不需要被一起序列化,将SerializableObject修改一下,在UnSerializableReference属性前加上transient修饰符:
private transient UnSerializableReference unSerializableReference; |
再运行以上SerializeDeserializeExample.main方法,输出结果如下:
2016-07-27 23:14:23.078 - INFO --- [ main] c.e.test.io.SerializeDeserializeExample [ 56] : test java.io.Serializable
2016-07-27 23:14:23.078 - INFO --- [ main] c.e.test.io.SerializeDeserializeExample [ 76] : SerializableObject{name='test java.io.Serializable', unSerializableReference=null}
手动将UnSerializableReference对象序列化和反序列化
上面通过transient修饰符指定UnSerializableReference属性不需要序列化,从而避免SerializableObject序列化失败,但如果一定也要求将UnSerializableReference对象序列化,但又不能改源码的情况下,要用到以下2个重要方法,这2个方法在对象序列化和反序列化时会通过java反射来执行:
private void writeObject(ObjectOutputStream os) throws IOException, ClassNotFoundException {} |
private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException {} |
修改SerializableObject实现上述2个方法,注意2个方法写入和读取属性数据时保持顺序一致:
public class SerializableObject implements Serializable { private static final long serialVersionUID = 1L; private String name; private transient UnSerializableReference unSerializableReference; public String getName() { return name; } public void setName(String name) { this.name = name; } public UnSerializableReference getUnSerializableReference() { return unSerializableReference; } public void setUnSerializableReference(UnSerializableReference unSerializableReference) { this.unSerializableReference = unSerializableReference; } private void writeObject(ObjectOutputStream os) throws IOException, ClassNotFoundException { try { os.defaultWriteObject(); os.writeObject(unSerializableReference.getName()); } catch (Exception e) { e.printStackTrace(); } } private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException { try { is.defaultReadObject(); String unSerializableReferenceName = (String) is.readObject(); unSerializableReference = new UnSerializableReference(); unSerializableReference.setName(unSerializableReferenceName); } catch (Exception e) { e.printStackTrace(); } } public String toString() { return "SerializableObject{" + "name='" + name + '\'' + ", unSerializableReference=" + unSerializableReference + '}'; }} |
再运行以上SerializeDeserializeExample.main方法,输出结果如下,UnSerializableReference已经正确序列化和反序列化:
2016-07-27 23:33:15.543 - INFO --- [ main] c.e.test.io.SerializeDeserializeExample [ 56] : test java.io.Serializable
2016-07-27 23:33:15.547 - INFO --- [ main] c.e.test.io.SerializeDeserializeExample [ 74] : SerializableObject{name='test java.io.Serializable', unSerializableReference=UnSerializableReference{name='unSerializableReference'}}
对象序列化时,writeObject方法的调用栈:
- ObjectOutputStream.writeObject
- ObjectOutputStream.writeObject0
- ObjectOutputStream.writeOrdinaryObject
- ObjectOutputStream.writeSerialData
- ObjectStreamClass.invokeWriteObject: Invokes the writeObject method of the represented serializable class.
- Method.invoke: private void com.example.test.io.SerializableObject.writeObject(java.io.ObjectOutputStream) throws java.io.IOException,java.lang.ClassNotFoundException
对象反序列化时,readObject方法的调用栈:
- ObjectInputStream.readObject
- ObjectInputStream.readObject0
- ObjectInputStream.readOrdinaryObject
- ObjectInputStream.readSerialData
- ObjectStreamClass.invokeReadObject: Invokes the readObject method of the represented serializable class.
- Method.invoke: private void com.example.test.io.SerializableObject.readObject(java.io.ObjectInputStream) throws java.io.IOException,java.lang.ClassNotFoundException
如果父类是可序列化的,那么子类都是可序列化的
但如果父类是可序列化的,却不想子类是可序列化的,则需要重写writeObject和readObject这2个方法,在方法体内抛出NotSerializableException异常即可,参数名为当前class的名字:
throw new NotSerializableException(getClass().getCanonicalName()); |
如果父类是不可序列化的,而子类实现了java.io.Serializable接口
这种情况下,子类只能序列化和反序列化自己定义的属性,而父类中的属性不能被序列化和反序列化。
如下父类不能序列化,并且有提供默认的无参构造方法:
public class UnSerializableParent { private String parent; public String getParent() { return parent; } public void setParent(String parent) { this.parent = parent; } public String toString() { return "UnSerializableParent{" + "parent='" + parent + '\'' + '}'; }} |
而SerializableObject继承了上面的UnSerializableParent类:
public class SerializableObject extends UnSerializableParent implements Serializable { private static final long serialVersionUID = 1L; private String name; private transient UnSerializableReference unSerializableReference; public String getName() { return name; } public void setName(String name) { this.name = name; } public UnSerializableReference getUnSerializableReference() { return unSerializableReference; } public void setUnSerializableReference(UnSerializableReference unSerializableReference) { this.unSerializableReference = unSerializableReference; } private void writeObject(ObjectOutputStream os) throws IOException, ClassNotFoundException { try { os.defaultWriteObject(); os.writeObject(unSerializableReference.getName()); } catch (Exception e) { e.printStackTrace(); } } private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException { try { is.defaultReadObject(); String unSerializableReferenceName = (String) is.readObject(); unSerializableReference = new UnSerializableReference(); unSerializableReference.setName(unSerializableReferenceName); } catch (Exception e) { e.printStackTrace(); } } public String toString() { return "SerializableObject{" + "name='" + name + '\'' + ", parent='" + getParent() + '\'' + ", unSerializableReference=" + unSerializableReference + '}'; }} |
修改SerializeDeserializeExample类如下,并运行main方法:
public class SerializeDeserializeExample { private static final Logger LOGGER = LoggerFactory.getLogger(SerializeDeserializeExample.class); public static void serialize(SerializableObject serializableObject) throws FileNotFoundException, IOException { File file = new File(System.getProperty("java.io.tmpdir") + "serializableObject.ser"); ObjectOutputStream objectOutputStream = null; try { objectOutputStream = new ObjectOutputStream(new FileOutputStream(file)); objectOutputStream.writeObject(serializableObject); objectOutputStream.flush(); } finally { if (objectOutputStream != null) { objectOutputStream.close(); } } } public static SerializableObject deSerialize() throws FileNotFoundException, IOException, ClassNotFoundException { ObjectInputStream objectInputStream = null; try { File file = new File(System.getProperty("java.io.tmpdir") + "serializableObject.ser"); objectInputStream = new ObjectInputStream(new FileInputStream(file)); Object object = objectInputStream.readObject(); SerializableObject deserializedObject = (SerializableObject) object; LOGGER.info(deserializedObject .getName()); return deserializedObject; } finally { if (objectInputStream != null) { objectInputStream.close(); } } } public static void main(String[] args) throws FileNotFoundException, ClassNotFoundException, IOException { SerializableObject serializableObject = new SerializableObject(); serializableObject.setName("test java.io.Serializable"); serializableObject.setParent("parent is not serializable"); UnSerializableReference unSerializableReference = new UnSerializableReference(); unSerializableReference.setName("unSerializableReference"); serializableObject.setUnSerializableReference(unSerializableReference); serialize(serializableObject); SerializableObject deSerializedObject = deSerialize(); LOGGER.info("{}", deSerializedObject); }} |
结果如下,父类属性parent并不能被子类一起序列化和反序列化,输出结果中为null。
2016-07-28 00:12:56.640 - INFO --- [ main] c.e.test.io.SerializeDeserializeExample [ 56] : test java.io.Serializable
2016-07-28 00:12:56.643 - INFO --- [ main] c.e.test.io.SerializeDeserializeExample [ 75] : SerializableObject{name='test java.io.Serializable', parent='null', unSerializableReference=UnSerializableReference{name='unSerializableReference'}}
如果需要将父类这个属性序列化,那还是要借助writeObject和readObject这2个方法,修改SerializableObject类如下:
public class SerializableObject extends UnSerializableParent implements Serializable { private static final long serialVersionUID = 1L; private String name; private transient UnSerializableReference unSerializableReference; public String getName() { return name; } public void setName(String name) { this.name = name; } public UnSerializableReference getUnSerializableReference() { return unSerializableReference; } public void setUnSerializableReference(UnSerializableReference unSerializableReference) { this.unSerializableReference = unSerializableReference; } private void writeObject(ObjectOutputStream os) throws IOException, ClassNotFoundException { try { os.defaultWriteObject(); os.writeObject(unSerializableReference.getName()); os.writeObject(getParent()); } catch (Exception e) { e.printStackTrace(); } } private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException { try { is.defaultReadObject(); String unSerializableReferenceName = (String) is.readObject(); unSerializableReference = new UnSerializableReference(); unSerializableReference.setName(unSerializableReferenceName); String parent = (String) is.readObject(); setParent(parent); } catch (Exception e) { e.printStackTrace(); } } public String toString() { return "SerializableObject{" + "name='" + name + '\'' + ", parent='" + getParent() + '\'' + ", unSerializableReference=" + unSerializableReference + '}'; }} |
运行结果如下,parent属性被序列化和反序列化了:
2016-07-28 00:15:25.678 - INFO --- [ main] c.e.test.io.SerializeDeserializeExample [ 56] : test java.io.Serializable
2016-07-28 00:15:25.682 - INFO --- [ main] c.e.test.io.SerializeDeserializeExample [ 75] : SerializableObject{name='test java.io.Serializable', parent='parent is not serializable', unSerializableReference=UnSerializableReference{name='unSerializableReference'}}
不能序列化static属性
这个属于class级别,而不是对象级别的,因此不在对象序列化管理范围内。
序列化对单例的破坏
运行以下SerializableSingleton.main方法:
public class SerializableSingleton implements Serializable { private static final Logger LOGGER = LoggerFactory.getLogger(SerializableSingleton.class); private volatile static SerializableSingleton singleton; private SerializableSingleton() { } public static SerializableSingleton getSingleton() { if (singleton == null) { synchronized (SerializableSingleton.class) { if (singleton == null) { singleton = new SerializableSingleton(); } } } return singleton; } public static void main(String[] args) throws IOException, ClassNotFoundException { ObjectOutputStream oos = null; ObjectInputStream ois = null; try { File file = new File(System.getProperty("java.io.tmpdir") + "serializableSingleton.ser"); oos = new ObjectOutputStream(new FileOutputStream(file)); //Write Obj to file oos.writeObject(SerializableSingleton.getSingleton()); //Read Obj from file ois = new ObjectInputStream(new FileInputStream(file)); SerializableSingleton newInstance = (SerializableSingleton) ois.readObject(); //判断是否是同一个对象 LOGGER.info("is the same object : {}", newInstance == SerializableSingleton.getSingleton()); } finally { if (oos != null) { oos.close(); } if (ois != null) { ois.close(); } } }} |
结果输出为:
2016-07-28 00:46:29.377 - INFO --- [ main] c.example.test.io.SerializableSingleton [ 47] : is the same object : false
也就是通过对SerializableSingleton的序列化与反序列化操作得到的对象是一个新的对象,这就破坏了SerializableSingleton的单例性。
ObjectInputStream.readOrdinaryObject()方法如下:
private Object readOrdinaryObject(boolean unshared) throws IOException { //此处省略部分代码 Object obj; try { obj = desc.isInstantiable() ? desc.newInstance() : null; } catch (Exception ex) { throw (IOException) new InvalidClassException( desc.forClass().getName(), "unable to create instance").initCause(ex); } //此处省略部分代码 if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) { Object rep = desc.invokeReadResolve(obj); if (unshared && rep.getClass().isArray()) { rep = cloneArray(rep); } if (rep != obj) { handles.setObject(passHandle, obj = rep); } } return obj;} |
防止序列化破坏单例模式
只要在SerializableSingleton类中定义readResolve方法就可以解决该问题:
public class SerializableSingleton implements Serializable { private static final Logger LOGGER = LoggerFactory.getLogger(SerializableSingleton.class); private volatile static SerializableSingleton singleton; private SerializableSingleton() { } public static SerializableSingleton getSingleton() { if (singleton == null) { synchronized (SerializableSingleton.class) { if (singleton == null) { singleton = new SerializableSingleton(); } } } return singleton; } protected Object readResolve() { return singleton; } public static void main(String[] args) throws IOException, ClassNotFoundException { ObjectOutputStream oos = null; ObjectInputStream ois = null; try { File file = new File(System.getProperty("java.io.tmpdir") + "serializableSingleton.ser"); oos = new ObjectOutputStream(new FileOutputStream(file)); //Write Obj to file oos.writeObject(SerializableSingleton.getSingleton()); //Read Obj from file ois = new ObjectInputStream(new FileInputStream(file)); SerializableSingleton newInstance = (SerializableSingleton) ois.readObject(); //判断是否是同一个对象 LOGGER.info("is the same object : {}", newInstance == SerializableSingleton.getSingleton()); } finally { if (oos != null) { oos.close(); } if (ois != null) { ois.close(); } } }} |
readResolve方法调用栈:
- ObjectInputStream.readObject
- ObjectInputStream.readObject0
- ObjectInputStream.readOrdinaryObject
- ObjectStreamClass.invokeReadResolve
- Method.invoke: private java.lang.Object com.example.test.io.SerializableSingleton.readResolve()
以上代码运行结果:
2016-07-28 01:01:52.198 - INFO --- [ main] c.example.test.io.SerializableSingleton [ 51] : is the same object : true
Sonar 中与 Serializable 相关的规则
- Comparators should be "Serializable".
- The non-serializable super class of a "Serializable" class should have a no-argument constructor.
- "Serializable" inner classes of "Serializable" classes should be static.
- Fields in a "Serializable" class should either be transient or serializable.
- "readResolve" methods should be inheritable.
- "readObject" should not be "synchronized".
- Custom serialization method signatures should meet requirements.
public class Watermelon implements Serializable { // ... private void writeObject(java.io.ObjectOutputStream out) throws IOException {...} private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {...} private void readObjectNoData() throws ObjectStreamException {...} protected Object readResolve() throws ObjectStreamException {...} private Object writeReplace() throws ObjectStreamException {...} |
java.io.Externalizable
java.io.Externalizable继承自java.io.Serializable接口,源码:
public interface Externalizable extends java.io.Serializable { void writeExternal(ObjectOutput out) throws IOException; void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;} |
正如接口名字所提示的,对象序列化和反序列化由外部代码指定才会执行,ExternalizableObject如下代码:
public class ExternalizableObject implements Externalizable { private static final Logger LOGGER = LoggerFactory.getLogger(ExternalizableObject.class); private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void writeExternal(ObjectOutput out) throws IOException { LOGGER.info("begin serialize object."); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { LOGGER.info("begin deserialize object."); } public String toString() { return "ExternalizableObject{" + "name='" + name + '\'' + '}'; }} |
运行ExternalizableSerializeExample.main方法如下:
public class ExternalizableSerializeExample { private static final Logger LOGGER = LoggerFactory.getLogger(ExternalizableSerializeExample.class); public static void serialize(ExternalizableObject externalizableObject) throws FileNotFoundException, IOException { File file = new File(System.getProperty("java.io.tmpdir") + "externalizableObject.ser"); ObjectOutputStream objectOutputStream = null; try { objectOutputStream = new ObjectOutputStream(new FileOutputStream(file)); objectOutputStream.writeObject(externalizableObject); objectOutputStream.flush(); } finally { if (objectOutputStream != null) { objectOutputStream.close(); } } } public static ExternalizableObject deSerialize() throws FileNotFoundException, IOException, ClassNotFoundException { ObjectInputStream objectInputStream = null; try { File file = new File(System.getProperty("java.io.tmpdir") + "externalizableObject.ser"); objectInputStream = new ObjectInputStream(new FileInputStream(file)); Object object = objectInputStream.readObject(); ExternalizableObject deserializedObject = (ExternalizableObject) object; LOGGER.info(deserializedObject.getName()); return deserializedObject; } finally { if (objectInputStream != null) { objectInputStream.close(); } } } public static void main(String[] args) throws IOException, ClassNotFoundException { ExternalizableObject externalizableObject = new ExternalizableObject(); externalizableObject.setName("externalizableObject"); serialize(externalizableObject); ExternalizableObject deSerializedObject = deSerialize(); LOGGER.info("{}", deSerializedObject); }} |
运行结果如下,发现对象并没有被序列化:
2016-07-28 22:14:53.932 - INFO --- [ main] c.example.test.io.ExternalizableObject [ 30] : begin serialize object.
2016-07-28 22:14:53.937 - INFO --- [ main] c.example.test.io.ExternalizableObject [ 35] : begin deserialize object.
2016-07-28 22:14:53.938 - INFO --- [ main] c.e.t.io.ExternalizableSerializeExample [ 36] : null
2016-07-28 22:14:53.939 - INFO --- [ main] c.e.t.io.ExternalizableSerializeExample [ 51] : ExternalizableObject{name='null'}
JVM在序列化对象时,发现类实现了接口Externalizable,那么它会调用此对象的writeExternal方法,在反序列化时,则会调用readExternal方法,并不会像前面实现Serializable接口那样通过ObjectOutputStream和ObjectInputStream那样进行对象序列化和反序列化。
还有一点值得注意:在使用Externalizable进行序列化的时候,在读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。所以,实现Externalizable接口的类必须要提供一个public的无参的构造器,否则会有类似以下错误提示:
Exception in thread "main" java.io.InvalidClassException: com.example.test.io.ExternalizableObject; no valid constructor
修改ExternalizableObject代码如下:
public class ExternalizableObject implements Externalizable { private static final Logger LOGGER = LoggerFactory.getLogger(ExternalizableObject.class); private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void writeExternal(ObjectOutput out) throws IOException { LOGGER.info("begin serialize object."); out.writeObject(name); } /** * The readExternal method must read the values in the same sequence * and with the same types as were written by writeExternal. */ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { LOGGER.info("begin deserialize object."); name = (String) in.readObject(); } public String toString() { return "ExternalizableObject{" + "name='" + name + '\'' + '}'; }} |
再次运行ExternalizableSerializeExample.main方法后,输出如下:
2016-07-28 22:31:56.044 - INFO --- [ main] c.example.test.io.ExternalizableObject [ 30] : begin serialize object.
2016-07-28 22:31:56.055 - INFO --- [ main] c.example.test.io.ExternalizableObject [ 36] : begin deserialize object.
2016-07-28 22:31:56.057 - INFO --- [ main] c.e.t.io.ExternalizableSerializeExample [ 36] : externalizableObject
2016-07-28 22:31:56.058 - INFO --- [ main] c.e.t.io.ExternalizableSerializeExample [ 52] : ExternalizableObject{name='externalizableObject'}
之所以提供Externalizable这个接口,主要是为了定制对象的序列化和反序列化,另外也有性能方面的提升,毕竟Serializable会通过java反射来执行序列化和反序列化,并且会将所有引用对象都进行序列化操作。
父类没有实现Externalizable
只要在writeExternal和readExternal方法里以相同的顺序写入和读取父类属性即可:
public class ExternalizableObject extends UnSerializableParent implements Externalizable { private static final Logger LOGGER = LoggerFactory.getLogger(ExternalizableObject.class); private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void writeExternal(ObjectOutput out) throws IOException { LOGGER.info("begin serialize object."); out.writeObject(name); out.writeObject(getParent()); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { LOGGER.info("begin deserialize object."); name = (String) in.readObject(); String parent = (String) in.readObject(); setParent(parent); } public String toString() { return "ExternalizableObject{" + "name='" + name + '\'' + ", parent='" + getParent() + '\'' + '}'; }} |
如果父类也实现了Externalizable接口,则子类的2个实现方法中通过super.writeExternal(out)和super.readExternal(in)调用父类的方法实现即可。
序列化的数据可以被签名和密封
如果需要对整个对象进行加密和签名,可以通过使用writeObject和readObject可以实现密码加密和签名管理,但最简单的是将它放在一个javax.crypto.SealedObject和java.security.SignedObject包装器中。两者都是可序列化的,所以将对象包装在SealedObject中,可以围绕原对象创建一个Wrapper,必须有对称密钥才能解密,而且密钥必须单独管理。同样,也可以将SignedObject用于数据验证,并且对称密钥也必须单独管理。