java object serialization

Java 序列化简介

Java对象序列化JDK 1.1中引入的一组开创性特性之一,是Java语言内建的一种对象持久化方式,用于作为一种将Java对象的状态转换为字节数组,以便存储或传输的机制,以后仍可以将字节数组转换回Java对象原有的状态。

实际上,序列化的思想是冻结对象状态,传输对象状态,如写到磁盘或者通过网络使用RMI远程方法调用等,然后解冻对象状态,重新获得可用的Java对象。

相关接口及类

Java为了方便开发人员将Java对象进行序列化及反序列化提供了一套方便的API来支持,其中包括以下接口和类:

  1. java.io.Serializable
  2. java.io.Externalizable
  3. java.io.ObjectOutput
  4. java.io.ObjectInput
  5. java.io.ObjectOutputStream
  6. java.io.ObjectInputStream
  7. javax.crypto.SealedObject
  8. 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;    }    @Override    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个类:ObjectInputStreamObjectOutputStream,其中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;    }    @Override    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必须使用staticfinal修饰,并且为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;    }    @Override    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;    }    @Override    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;    }    @Override    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();        }    }    @Override    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方法的调用栈:

  1. ObjectOutputStream.writeObject
  2. ObjectOutputStream.writeObject0
  3. ObjectOutputStream.writeOrdinaryObject
  4. ObjectOutputStream.writeSerialData
  5. ObjectStreamClass.invokeWriteObject: Invokes the writeObject method of the represented serializable class.
  6. Method.invoke: private void com.example.test.io.SerializableObject.writeObject(java.io.ObjectOutputStream) throws java.io.IOException,java.lang.ClassNotFoundException

对象反序列化时,readObject方法的调用栈:

  1. ObjectInputStream.readObject
  2. ObjectInputStream.readObject0
  3. ObjectInputStream.readOrdinaryObject
  4. ObjectInputStream.readSerialData
  5. ObjectStreamClass.invokeReadObject: Invokes the readObject method of the represented serializable class.
  6. Method.invoke: private void com.example.test.io.SerializableObject.readObject(java.io.ObjectInputStream) throws java.io.IOException,java.lang.ClassNotFoundException

如果父类是可序列化的,那么子类都是可序列化的

但如果父类是可序列化的,却不想子类是可序列化的,则需要重写writeObjectreadObject这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;    }    @Override    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();        }    }    @Override    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'}}

如果需要将父类这个属性序列化,那还是要借助writeObjectreadObject这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();        }    }    @Override    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方法调用栈:

  1. ObjectInputStream.readObject
  2. ObjectInputStream.readObject0
  3. ObjectInputStream.readOrdinaryObject
  4. ObjectStreamClass.invokeReadResolve
  5. 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 相关的规则

  1. Comparators should be "Serializable".
  2. The non-serializable super class of a "Serializable" class should have a no-argument constructor.
  3. "Serializable" inner classes of "Serializable" classes should be static.
  4. Fields in a "Serializable" class should either be transient or serializable.
  5. "readResolve" methods should be inheritable.
  6. "readObject" should not be "synchronized".
  7. 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;    }    @Override    public void writeExternal(ObjectOutput out) throws IOException {        LOGGER.info("begin serialize object.");    }    @Override    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {        LOGGER.info("begin deserialize object.");    }    @Override    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接口那样通过ObjectOutputStreamObjectInputStream那样进行对象序列化和反序列化。

还有一点值得注意:在使用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;    }    @Override    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.     */    @Override    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {        LOGGER.info("begin deserialize object.");        name = (String) in.readObject();    }    @Override    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

只要在writeExternalreadExternal方法里以相同的顺序写入和读取父类属性即可:

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;    }    @Override    public void writeExternal(ObjectOutput out) throws IOException {        LOGGER.info("begin serialize object.");        out.writeObject(name);        out.writeObject(getParent());    }    @Override    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {        LOGGER.info("begin deserialize object.");        name = (String) in.readObject();        String parent = (String) in.readObject();        setParent(parent);    }    @Override    public String toString() {        return "ExternalizableObject{" +                "name='" + name + '\'' +                ", parent='" + getParent() + '\'' +                '}';    }}

如果父类也实现了Externalizable接口,则子类的2个实现方法中通过super.writeExternal(out)super.readExternal(in)调用父类的方法实现即可。

序列化的数据可以被签名和密封

如果需要对整个对象进行加密和签名,可以通过使用writeObjectreadObject可以实现密码加密和签名管理,但最简单的是将它放在一个javax.crypto.SealedObjectjava.security.SignedObject包装器中。两者都是可序列化的,所以将对象包装在SealedObject中,可以围绕原对象创建一个Wrapper,必须有对称密钥才能解密,而且密钥必须单独管理。同样,也可以将SignedObject用于数据验证,并且对称密钥也必须单独管理。

References

  1. Serialization in java
  2. 单例与序列化的那些事儿
  3. Java对象的序列化与反序列化