Java Serializable: obviously the interface on an empty thing

For Java serialization, and I have been staying in the most simple cognitive - put that to serialize the class that implements Serializbalethe interface on it. I do not want to do more research, because it will use on the line thing.

But over time, to see the Serializbalenumber of more and more, I had a strong interest in it. It is time to take the time to research the study.

01, first to point theory

Is a set of Java serialization pioneering introduction of the characteristics of JDK 1.1, for converting Java byte array object, to facilitate storage or transmission. After that, you can still convert the byte array back to the original state of Java objects.

Serialization idea is "frozen" state of the object, and then written to disk or transmitted over the network; deserialized idea is to "unfreeze" the object state, regained available Java objects.

Let's look at the sequence of Serializbaleinterface definition:

public interface Serializable {
}
复制代码

Obviously it is an empty interface Well, actually be able to ensure the realization of its "object class" is serialization and de-serialization?

02, some more practical

Before answering these questions, let's create a class (only two fields, and the corresponding getter/setter) for serialization and de-serialization.

class Wanger {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
复制代码

Again create a test class by ObjectOutputStreamthe "18-year-old king" is written to the file which, in fact, is a kind of serialization process; and through ObjectInputStream"18-year-old king" will be read out from the document, in fact, one sequence of the process reversed.

public class Test {

    public static void main(String[] args) {
      // 初始化
        Wanger wanger = new Wanger();
        wanger.setName("王二");
        wanger.setAge(18);
        System.out.println(wanger);

        // 把对象写到文件中
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("chenmo"));){
            oos.writeObject(wanger);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 从文件中读出对象
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("chenmo")));){
            Wanger wanger1 = (Wanger) ois.readObject();
            System.out.println(wanger1);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

}
复制代码

However, due to Wangernot implement Serializbalethe interface, so run the test class when an exception is thrown, the stack information is as follows:

java.io.NotSerializableException: com.cmower.java_demo.xuliehua.Wanger
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
	at com.cmower.java_demo.xuliehua.Test.main(Test.java:21)
复制代码

Along the stack information, we look at ObjectOutputStreamthe writeObject0()method. Source portion thereof as follows:

if (obj instanceof String) {
    writeString((String) obj, unshared);
} else if (cl.isArray()) {
    writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
    writeEnum((Enum<?>) obj, desc, unshared);
} else if (obj instanceof Serializable) {
    writeOrdinaryObject(obj, desc, unshared);
} else {
    if (extendedDebugInfo) {
        throw new NotSerializableException(
            cl.getName() + "\n" + debugInfoStack.toString());
    } else {
        throw new NotSerializableException(cl.getName());
    }
}
复制代码

In other words, ObjectOutputStreamthe sequence of the time, will determine the sequence of the target which is a type of string? Array? enumerate? Or Serializable, if not all, then throw NotSerializableException.

If Wangerimplements Serializablethe interface, you can serialize and deserialize the.

class Wanger implements Serializable{
    private static final long serialVersionUID = -2095916884810199532L;
    
    private String name;
    private int age;
}
复制代码

Specifically how to serialize it?

With ObjectOutputStreaman example of it, it will in turn call the serialization time writeObject()writeObject0()writeOrdinaryObject()writeSerialData()invokeWriteObject()defaultWriteFields().

private void defaultWriteFields(Object obj, ObjectStreamClass desc)
        throws IOException
    {
        Class<?> cl = desc.forClass();
        desc.checkDefaultSerialize();

        int primDataSize = desc.getPrimDataSize();
        desc.getPrimFieldValues(obj, primVals);
        bout.write(primVals, 0, primDataSize, false);

        ObjectStreamField[] fields = desc.getFields(false);
        Object[] objVals = new Object[desc.getNumObjFields()];
        int numPrimFields = fields.length - objVals.length;
        desc.getObjFieldValues(obj, objVals);
        for (int i = 0; i < objVals.length; i++) {
          
            try {
                writeObject0(objVals[i],
                             fields[numPrimFields + i].isUnshared());
            }
        }
    }
复制代码

That how deserialize it?

With ObjectInputStream, for example, it will in turn call the deserialization time readObject()readObject0()readOrdinaryObject()readSerialData()defaultReadFields().

private void defaultWriteFields(Object obj, ObjectStreamClass desc)
        throws IOException
    {
        Class<?> cl = desc.forClass();
        desc.checkDefaultSerialize();

        int primDataSize = desc.getPrimDataSize();
        desc.getPrimFieldValues(obj, primVals);
        bout.write(primVals, 0, primDataSize, false);

        ObjectStreamField[] fields = desc.getFields(false);
        Object[] objVals = new Object[desc.getNumObjFields()];
        int numPrimFields = fields.length - objVals.length;
        desc.getObjFieldValues(obj, objVals);
        for (int i = 0; i < objVals.length; i++) {
          
            try {
                writeObject0(objVals[i],
                             fields[numPrimFields + i].isUnshared());
            }
        }
    }
复制代码

I want to see it, you should suddenly realized "Oh," a cry. SerializableThe reason why the interface is defined as empty, because it only acts as a logo, to tell the program to achieve its objects can be serialized, but the real serialization and de-serialization does not need it to complete.

03, some more precautions

Speak straight to the point, staticand transientthe modified field will not be serialized.

why? Let's prove, again explain why.

First, Wangertwo additional fields classes.

class Wanger implements Serializable {
    private static final long serialVersionUID = -2095916884810199532L;

    private String name;
    private int age;

    public static String pre = "沉默";
    transient String meizi = "王三";

    @Override
    public String toString() {
        return "Wanger{" + "name=" + name + ",age=" + age + ",pre=" + pre + ",meizi=" + meizi + "}";
    }
}
复制代码

Next, print object before and after serialization and deserialization test class, and before and after the change deserialize serialized staticvalue field. Specific code as follows:

// 初始化
Wanger wanger = new Wanger();
wanger.setName("王二");
wanger.setAge(18);
System.out.println(wanger);

// 把对象写到文件中
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("chenmo"));){
        oos.writeObject(wanger);
    } catch (IOException e) {
        e.printStackTrace();
    }
   
    // 改变 static 字段的值
Wanger.pre ="不沉默";

// 从文件中读出对象
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("chenmo")));){
    Wanger wanger1 = (Wanger) ois.readObject();
    System.out.println(wanger1);
} catch (IOException | ClassNotFoundException e) {
    e.printStackTrace();
}
// Wanger{name=王二,age=18,pre=沉默,meizi=王三}
// Wanger{name=王二,age=18,pre=不沉默,meizi=null}

复制代码

From the comparison of results among we can find:

1) prior to serialization, preis "silent", after serialization, premodify the value "no silence", after deserialization, prethe value "no silence", rather than the state before the sequence of "silent."

why? Serializing the object's state is saved, and staticthe modified state of the fields belonging to the class, and therefore can prove serialization does not save staticthe modified field.

2)序列化前,meizi 的值为“王三”,反序列化后,meizi 的值为 null,而不是序列化前的状态“王三”。

为什么呢?transient 的中文字义为“临时的”(论英语的重要性),它可以阻止字段被序列化到文件中,在被反序列化后,transient 字段的值被设为初始值,比如 int 型的初始值为 0,对象型的初始值为 null

如果想要深究源码的话,你可以在 ObjectStreamClass 中发现下面这样的代码:

private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {
    Field[] clFields = cl.getDeclaredFields();
    ArrayList<ObjectStreamField> list = new ArrayList<>();
    int mask = Modifier.STATIC | Modifier.TRANSIENT;

    int size = list.size();
    return (size == 0) ? NO_FIELDS :
        list.toArray(new ObjectStreamField[size]);
}
复制代码

看到 Modifier.STATIC | Modifier.TRANSIENT,是不是感觉更好了呢?

04、再来点干货

除了 Serializable 之外,Java 还提供了一个序列化接口 Externalizable(念起来有点拗口)。

两个接口有什么不一样的吗?试一试就知道了。

首先,把 Wanger 类实现的接口 Serializable 替换为 Externalizable

class Wanger implements Externalizable {
	private String name;
	private int age;

	public Wanger() {

	}

	public String getName() {
		return name;
	}

	
	@Override
	public String toString() {
		return "Wanger{" + "name=" + name + ",age=" + age + "}";
	}

	@Override
	public void writeExternal(ObjectOutput out) throws IOException {

	}

	@Override
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

	}

}
复制代码

实现 Externalizable 接口的 Wanger 类和实现 Serializable 接口的 Wanger 类有一些不同:

1)新增了一个无参的构造方法。

使用 Externalizable 进行反序列化的时候,会调用被序列化类的无参构造方法去创建一个新的对象,然后再将被保存对象的字段值复制过去。否则的话,会抛出以下异常:

java.io.InvalidClassException: com.cmower.java_demo.xuliehua1.Wanger; no valid constructor
	at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:150)
	at java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:790)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1782)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
	at com.cmower.java_demo.xuliehua1.Test.main(Test.java:27)
复制代码

2)新增了两个方法 writeExternal()readExternal(),实现 Externalizable 接口所必须的。

然后,我们再在测试类中打印序列化前和反序列化后的对象。

// 初始化
Wanger wanger = new Wanger();
wanger.setName("王二");
wanger.setAge(18);
System.out.println(wanger);

// 把对象写到文件中
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("chenmo"));) {
	oos.writeObject(wanger);
} catch (IOException e) {
	e.printStackTrace();
}

// 从文件中读出对象
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("chenmo")));) {
	Wanger wanger1 = (Wanger) ois.readObject();
	System.out.println(wanger1);
} catch (IOException | ClassNotFoundException e) {
	e.printStackTrace();
}
// Wanger{name=王二,age=18}
// Wanger{name=null,age=0}
复制代码

从输出的结果看,反序列化后得到的对象字段都变成了默认值,也就是说,序列化之前的对象状态没有被“冻结”下来。

why? Because we do not have Wangerclass overrides concrete writeExternal()and readExternal()methods. Then how to rewrite it?

@Override
public void writeExternal(ObjectOutput out) throws IOException {
	out.writeObject(name);
	out.writeInt(age);
}

@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
	name = (String) in.readObject();
	age = in.readInt();
}
复制代码

1) call ObjectOutputa writeObject()method of the type string nameis written to the output stream;

2) calling ObjectOutputa writeInt()method integer agewritten to the output stream;

3) calling ObjectInputa readObject()method of the type string nameis read into the input stream;

4) call ObjectInputa readInt()method of the type string ageis read into the input stream;

And then run a test class, you will find that the object can normally serialization and de-serialization of.

Presequence of: Wanger {name = king, age = 18} of the sequence: Wanger {name = king, age = 18}

05, like some dessert

Let me ask you, do you know private static final long serialVersionUID = -2095916884810199532L;the role of this code do?

Ok......

serialVersionUIDIt is known as serialization ID, which is to determine whether the anti-Java object serialization important success factor. When deserialization, Java virtual machine to the byte stream serialVersionUIDwith the serialized class in serialVersionUIDcomparison, if the same may be deserialized, otherwise it will throw an exception inconsistent serialized version.

When a class implements Serializablethe interface, IDE will remind preferably generates a sequence of the class ID, as follows:

1) add a default version of the serialization ID:

private static final long serialVersionUID = 1L复制代码

2) does not overlap the sequence of adding a randomly generated ID.

private static final long serialVersionUID = -2095916884810199532L;
复制代码

3) add @SuppressWarningscomments.

@SuppressWarnings("serial")
复制代码

How to choose?

First, we use the second option, add serialization ID is a randomly generated sequence of classes.

class Wanger implements Serializable {
	private static final long serialVersionUID = -2095916884810199532L;
	
	private String name;
	private int age;

	// 其他代码忽略
}
复制代码

Then, a sequence of Wangerobjects in a file.

// 初始化
Wanger wanger = new Wanger();
wanger.setName("王二");
wanger.setAge(18);
System.out.println(wanger);

// 把对象写到文件中
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("chenmo"));) {
	oos.writeObject(wanger);
} catch (IOException e) {
	e.printStackTrace();
}
复制代码

At this time, we slipped the Wangerserialization class ID willed it, hehe.

// private static final long serialVersionUID = -2095916884810199532L;
private static final long serialVersionUID = -2095916884810199533L;
复制代码

Well, ready to deserialize it.

try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("chenmo")));) {
	Wanger wanger = (Wanger) ois.readObject();
	System.out.println(wanger);
} catch (IOException | ClassNotFoundException e) {
	e.printStackTrace();
}
复制代码

Oops, wrong.

java.io.InvalidClassException:  local class incompatible: stream classdesc 
serialVersionUID = -2095916884810199532,
local class serialVersionUID = -2095916884810199533
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521)
	at com.cmower.java_demo.xuliehua1.Test.main(Test.java:27)
复制代码

Exception stack inside information tells us that persistent inconsistencies read from a file inside the serialization serialization ID and local ID, not deserialize.

That if we adopt the third method is Wangerto add a class @SuppressWarnings("serial")comment it?

@SuppressWarnings("serial")
class Wanger3 implements Serializable {
// 省略其他代码
}
复制代码

Well, again deserialize it. Unfortunately, still error.

java.io.InvalidClassException:  local class incompatible: stream classdesc 
serialVersionUID = -2095916884810199532, 
local class serialVersionUID = -3818877437117647968
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521)
	at com.cmower.java_demo.xuliehua1.Test.main(Test.java:27)
复制代码

Exception stack information which tells us that the local serialized ID is -3,818,877,437,117,647,968, and persistent file inside to read the serialized ID remains inconsistent, not deserialize. What is this indicating? Use @SuppressWarnings("serial")of the annotation, the annotation may be serialized for the class automatically generates a random sequence of ID.

It can be proved, Java virtual machine is allowed to de-serialization, depends not only on the class path and function codes match, there is a very important factor is whether the ID sequence of the same.

That is, if there are no special requirements, the default serialization ID (1L) can be, so you can ensure the success of the anti-sequence codes coincide.

class Wanger implements Serializable {
	private static final long serialVersionUID = 1L;
// 省略其他代码
}
复制代码

06, some more summary

Before writing this article, I really did not expect: "empty their body" of Serializableyet there are so many content can be studied!

After writing this article, I could not help but think of science scholar Caolin Jing said a word to say: "In the study then a small problem to live, and each must summarize knowledge" - say Barbara Barbara correct!


Guess you like

Origin juejin.im/post/5d0c4bebf265da1bc23f7f1d