JDK1.8源码笔记(4) Serializable

关于Serializable

Serializability of a class is enabled by the class implementing the
 java.io.Serializable interface.
一个类序列化的能力通过实现java.io.Serializable接口可以开启。
Classes that do not implement this interface will not have any of their state serialized or deserialized.
如果不实现的话,是没有办法序列化和反序列化的。

All subtypes of a serializable class are themselves serializable.
可序列化类的子类都是可以序列化的。

The serialization interface has no methods or fields and serves only to identify the semantics of being serializable.
能注意到这个接口也是一个空的接口。只是为了表明语义,和Cloneable类似。

* To allow subtypes of non-serializable classes to be serialized, the
* subtype may assume responsibility for saving and restoring the
* state of the supertype's public, protected, and (if accessible)
* package fields.  The subtype may assume this responsibility only if
* the class it extends has an accessible no-arg constructor to
* initialize the class's state.  It is an error to declare a class
* Serializable if this is not the case.  The error will be detected at
* runtime.
若子类可序列化,子类坑需要承担保存和恢复父类字段的责任。所以父类需要无参的构造方法才可以,不然子类序列化过程中会抛出InvalidClassException异常。
https://stackoverflow.com/questions/25341555/serialization-issue-if-the-parent-does-not-have-a-no-arg-constructor/25341871#25341871

* During deserialization, the fields of non-serializable classes will
* be initialized using the public or protected no-arg constructor of
* the class.  A no-arg constructor must be accessible to the subclass
* that is serializable.  The fields of serializable subclasses will
* be restored from the stream.
在反序列化的过程中,不可序列化(如不可序列化的父类中的字段)的字段会通过这个类中public或protected
修饰的无参构造方法进行初始化。所以一个无参的构造方法必须能够开放给需要序列化的子类去访问。子类的字段会从stream(流)中恢复。

* When traversing a graph, an object may be encountered that does not
* support the Serializable interface. In this case the
* NotSerializableException will be thrown and will identify the class
* of the non-serializable object.
当传输图片的时候,一个对象可能会遇到不支持序列化的接口。这样的话会抛出一个指明不可序列化对象的NotSerializableException异常。

* Classes that require special handling during the serialization and
* deserialization process must implement special methods with these exact
* signatures:
类的序列化和反序列化过程必须通过实现下面这些方法进行处理:

* private void writeObject(java.io.ObjectOutputStream out)
*     throws IOException
* private void readObject(java.io.ObjectInputStream in)
*     throws IOException, ClassNotFoundException;
* private void readObjectNoData()
*     throws ObjectStreamException;

* The writeObject method is responsible for writing the state of the
* object for its particular class so that the corresponding
* readObject method can restore it. 
writeObject方法的作用是为特定的类写入状态以便相应的readObject方法可以读出。

* The readObject method is responsible for reading from the stream and
* restoring the classes fields.
readObject的作用是从stream中获得类的字段。

* The readObjectNoData method is responsible for initializing the state of
* the object for its particular class in the event that the serialization
* stream does not list the given class as a superclass of the object being
* deserialized.

Points to remember
1. If a parent class has implemented Serializable interface then child class doesn’t need to implement it but vice-versa is not true.
2. Only non-static data members are saved via Serialization process.
3. Static data members and transient data members are not saved via Serialization process.So, if you don’t want to save value of a non-static data member then make it transient.
4. Constructor of object is never called when an object is deserialized.
5. Associated objects must be implementing Serializable interface.
1 父类继承Serializable,子类同样能序列化。
2 只有实例变量才会保存通过Serialization过程。
3 如果你不希望一个实例变量被序列化,make it transient。
4 反序列化的过程中Constructor never use。
5 相关的类都必须实现Serializable接口。

关于SerialVersionUID

The Serialization runtime associates a version number with each Serializable class called a SerialVersionUID, which is used during Deserialization to verify that sender and reciever of a serialized object have loaded classes for that object which are compatible with respect to serialization.
序列化过程中把为每一个Serializable class tag一个被称为SerialVersionUID的version number。SerialVersionUID是在反序列化的过程去验证sender和reciever都加载了这个对象相应的compatible的类。
If the reciever has loaded a class for the object that has different UID than that of corresponding sender’s class, the Deserialization will result in an InvalidClassException. A Serializable class can declare its own UID explicitly by declaring a field name.
如果reciever收到的SerialVersionUID和自己的不对付,会导致InvalidClassException。一个可序列化的类可以声明自己的UID通过声明一个变量。

If a serializable class doesn’t explicitly declare a serialVersionUID, then the serialization runtime will calculate a default one for that class based on various aspects of class, as described in Java Object Serialization Specification. However it is strongly recommended that all serializable classes explicitly declare serialVersionUID value, since its computation is highly sensitive to class details that may vary depending on compiler implementations, any change in class or using different id may affect the serialized data.
如果可序列化对象没有显式声明一个serialVersionUID,serialization过程中也会自动计算出一个serialVersionUID,但是我们strongly recommended自己声明一个,因为计算这种方法是detail sensitive甚至可能依赖于便起的实现不同。

Why SerialVersionUID is static?
Since the property is bound to the class, it has to be made static.
The how to transfer SerialVersionUID?
It is saved in ObjectOutputStream class descriptor.

关于tranisent

transient keyword plays an important role to meet security constraints. There are various real-life examples where we don’t want to save private data in file. Another use of transient keyword is not to serialize the variable whose value can be calculated/derived using other serialized objects or system such as age of a person, current date, etc.
transient扮演在安全限制中扮演一个重要角色。比如我们不想把private data进行传入,或者那些能够被计算或导出的值。
Practically we serialized only those fields which represent a state of instance, after all serialization is all about to save state of an object to a file. It is good habit to use transient keyword with private confidential fields of a class during serialization.

transient用来修饰static和final变量没用,当如果修饰的话也能通过编译。

例子如下:
import java.io.*; 
class Test implements Serializable 

    // Normal variables 
    int i = 10, j = 20; 

    // Transient variables 
    transient int k = 30; 

    // Use of transient has no impact here 
    transient static int l = 40; 
    transient final int m = 50; 

    public static void main(String[] args) throws Exception 
    { 
        Test input = new Test(); 

        // serialization 
        FileOutputStream fos = new FileOutputStream("abc.txt"); 
        ObjectOutputStream oos = new ObjectOutputStream(fos); 
        oos.writeObject(input); 

        // de-serialization 
        FileInputStream fis = new FileInputStream("abc.txt"); 
        ObjectInputStream ois = new ObjectInputStream(fis); 
        Test output = (Test)ois.readObject(); 
        System.out.println("i = " + output.i); 
        System.out.println("j = " + output.j); 
        System.out.println("k = " + output.k); 
        System.out.println("l = " + output.l); 
        System.out.println("m = " + output.m); 
    } 

结果:
i = 10
j = 20
k = 0
l = 40
m = 50
k的值没有被存下来,而是用0这个int的实例变量默认值赋给他。

你可能感兴趣的:(JDK1.8源码笔记(4) Serializable)