"151 Suggestions for Improving Java Programs" Reading Notes Display Statement UID

Recommendation 11: Develop a good habit and explicitly declare the UID
We write a class that implements the Serializable interface (serialization flag interface), and Eclipse will immediately give
a yellow warning: a Serial Version ID needs to be added. Why increase? How is it calculated?
What is the use? This chapter will explain the problem.
The purpose of a class implementing the Serializable interface is for persistence, such as network transmission or local storage, to
provide prerequisite support for the system's document generation, distribution and heterogeneous deployment.
Without serialization, the familiar remote calls and object databases cannot exist now. Let's look at a simple serialization class: public class Person implements Serializable{ private String name ; /* The getter/setter method of the name property is omitted * / } This is a simple JavaBean that implements the Serializable interface and can be transmitted over the network, or stored and then read. Here we use Java Message Service (Java Message Service) to transfer the object (that is, to transfer an object through the network), the data type defined in the message queue is ObjectMessage, first define a message producer (Producer), the code is as follows: public class Producer{ public static void main











( String[]args ) throws Exception{
Person person=new Person
();
person.setName ( "The Demon King " );
// Serialization, save to disk
SerializationUtils.writeObject ( person );
} }
A tool class is introduced here SerializationUtils, whose function is to serialize and deserialize a class
, and store it on the hard disk (simulating network transmission), its code is as follows:
public class SerializationUtils{
private static String FILE_NAME="c
: /obj.bin" ;
/ / serialize
public static void writeObject ( Serializable s ) {
try{
ObjectOutputStream oos=new ObjectOutputStream
( new
FileOutputStream
FILE_NAME ));
oos.writeObject s ); oos.close();}catchException e{e.printStackTrace();} }public static Object readObject(){Object obj=null//反序列化try{Object Input in put=new Object Input StreamnewFileInputStreamFILE_NAME));obj=input.readObject();input.close();}catchException e{













e.printStackTrace
();
}
return obj
;
} }
Through the object serialization process, an object is converted from a memory block to a transmittable data stream, and then sent
to the message consumer (Consumer) through the network, and deserialized , generate an instance object, the code is as follows:
public class Consumer{
public static void main
( String[]args ) throws Exception{
//
Deserialize
Person p= ( Person ) SerializationUtils.readObject ();
System.out.println ( "name ="+p.getName ());
} }
This is a deserialization process, that is, the process of converting the object data stream into an instance object, and the output is: The Demon King of Chaos. This is too easy, yes, this is a typical demo of serialization and deserialization. But here is a hidden question: what kind of magic happens if there is a difference in the class (the Person class) referenced by the producer of the message and the consumer of the message? For example: the Person class in the message producer adds an age attribute, but the consumer does not add this




属性。为啥没有增加?!因为这是个分布式部署的应用,你甚至都不知道这个应用部署在何
处,特别是通过广播(broadcast)方式发送消息的情况,漏掉一两个订阅者也是很正常
的。
在这种序列化和反序列化的类不一致的情形下,反序列化时会报一个
InvalidClassException异常,原因是序列化和反序列化所对应的类版本发生了变化,JVM不
能把数据流转换为实例对象。接着刨根问底:JVM是根据什么来判断一个类版本的呢?
好问题,通过SerialVersionUID,也叫做流标识符(Stream Unique Identifier),即
类的版本定义的,它可以显式声明也可以隐式声明。显式声明格式如下:
private static final long serialVersionUID=XXXXXL
而隐式声明则是我不声明,你编译器在编译的时候帮我生成。生成的依据是通过包名、
类名、继承关系、非私有的方法和属性,以及参数、返回值等诸多因子计算得出的,极度复
杂,基本上计算出来的这个值是唯一的。
serialVersionUID如何生成已经说明了,我们再来看看serialVersionUID的作用。JVM
在反序列化时,会比较数据流中的serialVersionUID与类的serialVersionUID是否相同,如
果相同,则认为类没有发生改变,可以把数据流load为实例对象;如果不相同,对不起,我
JVM不干了,抛个异常InvalidClassException给你瞧瞧。这是一个非常好的校验机制,可以
保证一个对象即使在网络或磁盘中“滚过”一次,仍能做到“出淤泥而不染”,完美地实现
类的一致性。
但是,有时候我们需要一点特例场景,例如:我的类改变不大,JVM是否可以把我以前
的对象反序列化过来?就是依靠显式声明serialVersionUID,向JVM撒谎说“我的类版本没
有变更”,如此,我们编写的类就实现了向上兼容。我们修改一下上面的Person类,代码如
下:
public class Person implements Serializable{
private static fnal long serialVersionUID=55799L

/* 其他保持不变 */
}

刚开始生产者和消费者持有的Person类版本一致,都是V1.0,某天生产者的Person类版
本变更了,增加了一个“年龄”属性,升级为V2.0,而由于种种原因(比如程序员疏忽、升
级时间窗口不同等)消费端的Person还保持为V1.0版本,代码如下:
public class Person implements Serializable{
private static final long serialVersionUID=5799L

private int age
/*age name getter/setter 方法省略 */
}
此时虽然生产者和消费者对应的类版本不同,但是显式声明的serialVersionUID相同,
反序列化也是可以运行的,所带来的业务问题就是消费端不能读取到新增的业务属性(age
属性)而已。
通过此例,我们的反序列化实现了版本向上兼容的功能,使用V1.0版本的应用访问了一
个V2.0版本的对象,这无疑提高了代码的健壮性。我们在编写序列化类代码时,随手加上
serialVersionUID字段,也不会给我们带来太多的工作量,但它却可以在关键时候发挥异乎
寻常的作用。
注意 显式声明serialVersionUID可以避免对象不一致,但尽量不要以这种方式向
JVM“撒谎”。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325950402&siteId=291194637