我们在编写实现Serializable接口的类的时候,IDE会提示:需要增加一个Serial Version ID。
1 public class Person implements Serializable { 2 private String name; 3 /*name属性的getter/setter方法省略*/ 4 }
这里以Java消息服务方式传递该对象(即通过网络传递一个对象),定义在消息队列中数据类型为ObjectMessage,首先定义一个消息的生产者(Producer)。
1 public class Producer{ 2 public static void main(String[] args){ 3 Person person = new Person(); 4 person.setName("Kong"); 5 //序列化,保存到磁盘上, 6 SerializationUtils .writeObject(person); 7 } 8 }
这里引入了一个工具类SerializableUtils,其作用是对一个类进行序列化和反序列化,并存储在硬盘上。
1 public class SerializationUtils { 2 private static String FILE_NAME = "D:/obj.bin"; 3 //序列化 4 public static void writeObject(Serializable s){ 5 try{ 6 ObjectOutputStream oos = new ObjectOutputStream(new FlieOutputStream(FILE_NAME)); 7 oos.writeObject(s); 8 oos.close(); 9 } catch (Exception e) { 10 e.printStackTrace(); 11 } 12 } 13 14 public static Object readObject(){ 15 Object obj = null; 16 //反序列化 17 try{ 18 ObjectInputStream input = new ObjectInputStream(new FileInputStream(FILE_NAME)); 19 obj = input.readObject(); 20 input.close(); 21 } catch(Exception e) { 22 e.printStackTrace(); 23 } 24 return obj; 25 } 26 }
通过对象序列化过程,把一个对象从内存块转化为可传输的数据流,然后通过网络发送消息到消费者那里,并进行反序列化,生成实例对象。
public class Consumer{ public static void main (String[] args) throws Exception{ //反序列化 Person p = (Person) SerializationUtils.readObject(); System.out.println("name = "+ p.getName()); } }
这是序列化和反序列化得典型DEMO。
有个问题:如果消息的生产者和消息的消费者 所参考的类(Person)有差异会出现什么情况?比如:消息生产者中的Person类增加一个age属性,而消费者没有增加该属性。在这种序列化和反序列化不一样的情况下,反序列化会报一个InvalidClassException异常,原因是序列化和反序列化所对应的类版本发生了变化,JVM不能把数据流转换为实例对象。
那么JVM是通过什么来判断一个类的版本类型?
通过SerialVersionUID,它可以隐式声明和显示声明,显示声明如下:
private static final long serialVersionUID = xxxxL
隐式声明即不声明,是编译器在 编译的时候通过包名,类名,继承关系、非私有的方法和属性,以及参数,返回值等各种计算出来,可以说是唯一的值。
JVM在反序列化时会比较数据流的serialVersionUID与类中的serialVersionUID是否一样,如果相同,则认为类没有发什么改变呢,可以把数据流load为实例对象:如果不相同,则会报InvalidClassException异常。
但是有时候类的改动很小,想把以前的对象反序列化过来,则需要显示的声明serialVersionUID,向JVM撒谎我的类没有改变。
原文:https://www.cnblogs.com/woyaodangxueba/p/10480465.html