serialVersionUID作用全面解析

serialVersionUID适用于Java的序列化机制。简单来说,Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。

serialVersionUID有两种显示的生成方式:

一是默认的1L,比如:private static final long serialVersionUID = 1L;       
二是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如:

private static final long  serialVersionUID = xxxxL;

当一个类实现了Serializable接口,如果没有显示的定义serialVersionUID,Eclipse会提供相应的提醒。面对这种情况,我们只需要在Eclipse中点击类中warning图标一下,Eclipse就会      自动给定两种生成的方式。如果不想定义,在Eclipse的设置中也可以把它关掉的,设置如下:
Window ==> Preferences ==> Java ==> Compiler ==> Error/Warnings ==> Potential programming problems

将Serializable class without serialVersionUID的warning改成ignore即可。

当实现java.io.Serializable接口的类没有显式地定义一个serialVersionUID变量时候,Java序列化机制会根据编译的Class自动生成一个serialVersionUID作序列化版本比较用,这种情况下,如果Class文件(类名,方法明等)没有发生变化(增加空格,换行,增加注释等等),就算再编译多次,serialVersionUID也不会变化的。

如果我们不希望通过编译来强制划分软件版本,即实现序列化接口的实体能够兼容先前版本,就需要显式地定义一个名为serialVersionUID,类型为long的变量,不修改这个变量值的序列化实体都可以相互进行串行化和反串行化。

下面用代码说明一下serialVersionUID在应用中常见的几种情况。

(1)序列化实体类

import java.io.Serializable;
public class Person implements Serializable
{
  private static final long serialVersionUID = 1234567890L;
  public int id;
  public String name;

  public Person(int id, String name)
  {
    this.id = id;
    this.name = name;
  }

  public String toString()
  {
    return "Person: " + id + " " + name;
  }
}

(2)序列化功能:

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class SerialTest
{

  public static void main(String[] args) throws IOException
  {
    Person person = new Person(1234, "wang");
    System.out.println("Person Serial" + person);
    FileOutputStream fos = new FileOutputStream("Person.txt");
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    oos.writeObject(person);
    oos.flush();
    oos.close();
  }
}

(3)反序列化功能:

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class DeserialTest
{
  public static void main(String[] args) throws IOException, ClassNotFoundException
  {
    Person person;

    FileInputStream fis = new FileInputStream("Person.txt");
    ObjectInputStream ois = new ObjectInputStream(fis);
    person = (Person) ois.readObject();
    ois.close();
    System.out.println("Person Deserial" + person);
  }

}

情况一:假设Person类序列化之后,从A端传输到B端,然后在B端进行反序列化。在序列化Person和反序列化Person的时候,A端和B端都需要存在一个相同的类。如果两处的serialVersionUID不一致,会产生什么错误呢?

【答案】可以利用上面的代码做个试验来验证:

先执行测试类SerialTest,生成序列化文件,代表A端序列化后的文件,然后修改serialVersion值,再执行测试类DeserialTest,代表B端使用不同serialVersion的类去反序列化,结果报错:
Exception in thread "main" java.io.InvalidClassException: test.Person; local class incompatible: stream classdesc serialVersionUID = 1234567890, local class serialVersionUID = 123456789
    at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:560)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1580)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1493)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1729)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1326)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:348)
    at test.DeserialTest.main(DeserialTest.java:15)

情况二:假设两处serialVersionUID一致,如果A端增加一个字段,然后序列化,而B端不变,然后反序列化,会是什么情况呢?
【答案】新增 public int age; 执行SerialTest,生成序列化文件,代表A端。删除 public int age,反序列化,代表B端,最后的结果为:执行序列化,反序列化正常,但是A端增加的字段丢失(被B端忽略)。

情况三:假设两处serialVersionUID一致,如果B端减少一个字段,A端不变,会是什么情况呢?
【答案】序列化,反序列化正常,B端字段少于A端,A端多的字段值丢失(被B端忽略)。

情况四:假设两处serialVersionUID一致,如果B端增加一个字段,A端不变,会是什么情况呢?
验证过程如下:
先执行SerialTest,然后在实体类Person增加一个字段age,如下所示,再执行测试类DeserialTest.

import java.io.Serializable;
public class Person implements Serializable
{
  private static final long serialVersionUID = 123456789L;
  public int id;
  public String name;
  public int age;

  public Person(int id, String name)
  {
    this.id = id;
    this.name = name;
  }

  public String toString()
  {
    return "Person: " + id + " " + name;
  }
}

相应的修改测试类DeserialTest,打印出age的值。

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class SerialTest
{

  public static void main(String[] args) throws IOException
  {
    Person person = new Person(1234, "wang");
    System.out.println("Person Serial" + person + " age:" + person.age);
    FileOutputStream fos = new FileOutputStream("Person.txt");
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    oos.writeObject(person);
    oos.flush();
    oos.close();
  }
}

结果为:

Person Deserial Person: 1234 wang age: 0

说明序列化,反序列化正常,B端新增加的int字段被赋予了默认值0。
最后通过下面的图片,总结一下上面的几种情况。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 序列化版本号serialVersionUID的作用_动力节点Java学院整理

    Java序列化是将一个对象编码成一个字节流,反序列化将字节流编码转换成一个对象. 序列化是Java中实现持久化存储的一种方法:为数据传输提供了线路级对象表示法. Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的.在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常. Eclipse中The

  • 全面解释Java中的serialVersionUID

    serialVersionUID作用: 序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性. 有两种生成方式:一个是默认的1L,比如:private static final long serialVersionUID = 1L;一个是根据类名.接口名.成员方法及属性等来生成一个64位的哈希字段,比如:private static final   long     serialVersionUID = xxxxL; 当你一个类实现了Serializable接口,如果没有定义s

  • serialVersionUID作用全面解析

    serialVersionUID适用于Java的序列化机制.简单来说,Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的.在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException. serialVersionUID有两种显示的生成方式: 一是默认的1L,比如:pr

  • Java SerialVersionUID作用详解

    问题1:Serializable是什么 首先,说说Serializable是实现java将内存中的类存储至硬盘中而使用的 一个类使用了Serializalbe接口,在序列化到文件时,会有一个SerialVersionUID. 这个东东是用于对类进行版本控制的. 首先看Person类清单: ===== import java.io.Serializable; public class Person implements Serializable { //如果没有指定serialVersionUID

  • Java 负载均衡算法作用详细解析

    目录 前言 轮询算法 随机算法 加权随机算法 加权轮询算法 源地址hash算法 最小请求数算法 前言 负载均衡在Java领域中有着广泛深入的应用,不管是大名鼎鼎的nginx,还是微服务治理组件如dubbo,feign等,负载均衡的算法在其中都有着实际的使用 负载均衡的核心思想在于其底层的算法思想,比如大家熟知的算法有 轮询,随机,最小连接,加权轮询等,在现实中不管怎么配置,都离不开其算法的核心原理,下面将结合实际代码对常用的负载均衡算法做一些全面的总结. 轮询算法 轮询即排好队,一个接一个的轮着

  • c++中ref的作用示例解析

    目录 正文 示例1: 输出: 输出: 总结 正文 C++11 中引入 std::ref 用于取某个变量的引用,这个引入是为了解决一些传参问题. 我们知道 C++ 中本来就有引用的存在,为何 C++11 中还要引入一个 std::ref 了?主要是考虑函数式编程(如 std::bind)在使用时,是对参数直接拷贝,而不是引用.下面通过例子说明 示例1: #include <functional> #include <iostream> void f(int& n1, int&

  • C# DES加密算法中向量的作用详细解析

    DES一共就有4个参数参与运作:明文.密文.密钥.向量.为了初学者容易理解,可以把4个参数的关系写成:密文=明文+密钥+向量:明文=密文-密钥-向量.为什么要向量这个参数呢?因为如果有一篇文章,有几个词重复,那么这个词加上密钥形成的密文,仍然会重复,这给破解者有机可乘,破解者可以根据重复的内容,猜出是什么词,然而一旦猜对这个词,那么,他就能算出密钥,整篇文章就被破解了!加上向量这个参数以后,每块文字段都会依次加上一段值,这样,即使相同的文字,加密出来的密文,也是不一样的,算法的安全性大大提高!

  • Android中Parcelable的作用实例解析

    在android提供了一种类型:Parcel.被用作封装数据的容器,封装后的数据可以通过Intent或IPC传递. 除了基本类型以外,只有实现了Parcelable接口的类才能被放入Parcel中.   Parcelable实现要点:需要实现三个东西 1)writeToParcel 方法.该方法将类的数据写入外部提供的Parcel中.声明如下: writeToParcel (Parcel dest, int flags) 具体参数含义见javadoc 2)describeContents方法.没

  • Java中Volatile的作用实例解析

    Java 语言中的 volatile 变量可以被看作是一种 "程度较轻的 synchronized":与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分. 锁 锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility). 互斥即一次只允许一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的协调访问协议,这样,一次就只有一个线程能够使用

  • nginx中的listen指令实例解析

    剧情回顾 上一篇文章我们分析了location指令的解析过程,简单的回顾一下这个内容:每个location对应一个ngx_http_core_loc_conf_t结构体,所有的location通过一个双向队列连接在一起.数据结构比较复杂. listen指令 nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解.与网络有关的配置命令主要有两个:listen和sever_name.listen命令设置nginx监听地址,对于IP协议,这个

  • springmvc处理响应数据的解析

    1. ModelAndView 相关的成员变量和方法 private Object view; 描述视图信息 private ModelMap model 描述模型数据(响应数据) public void setViewName(String viewName) 设置视图信息 public ModelAndView addObject(String attributeName, Object attributeValue) 设置模型数据 protected Map<String, Object>

随机推荐