为什么Java中序列化的SerialVersionUID总是无意义的?

开发 后端
这个题目不主要讲serialVersionUID作用,而是讲后面的那一串数字的意义,当然也会对java的这个serialVersionUID的作用进行一个讲解。这篇文章是我积压了很久的一篇文章,写了一半,几个月了才发现,于是拿出来好好整理一下。

[[360997]]

这个题目不主要讲serialVersionUID作用,而是讲后面的那一串数字的意义,当然也会对java的这个serialVersionUID的作用进行一个讲解。这篇文章是我积压了很久的一篇文章,写了一半,几个月了才发现,于是拿出来好好整理一下。

一、serialVersionUID的作用

通过java进行网络之间的数据传输是不能直接把对象进行传的,需要在发送端把数据切分,在接收端对切分的数据进行重装。这种切分和重装的方式就叫做序列化。下面我们举一个例子:

(1)不指定serialVersionUID

首先我们定义一个User类,继承Serializable接口

 

然后序列化

  1. public static void main(String[] args) throws Exception { 
  2.         // 序列化 
  3.         User an = new User(); 
  4.         FileOutputStream fos = new FileOutputStream("user"); 
  5.         ObjectOutputStream oos = new ObjectOutputStream(fos); 
  6.         oos.writeObject(an); 
  7.         oos.close(); 
  8.     } 

反序列化

  1. public static void main(String[] args) throws Exception { 
  2.         // 反序列化 
  3.         FileInputStream fis = new FileInputStream("user"); 
  4.         ObjectInputStream ois = new ObjectInputStream(fis); 
  5.         User u = (User)ois.readObject(); 
  6.         System.out.println(u.name+"  " + u.age); 
  7.         ois.close(); 
  8.         fis.close(); 
  9.     } 

现在我们举了一个序列化的例子,没有指定serialVersionUID,此时程序在编译的时候就会自动为我们生成一个ID号,整个过程是这样的:

(1)发送端不指定serialVersionUID,编译器为我们默认生成,并序列化保存在流中发送到接收端。

(2)接收端把serialVersionUID保存起来,进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化。也就是说传过来的ID和本地ID不一致时候就会出现错误。

现在验证一下第二种情况:

 

我们再去反序列化的时候,因为JVM会把传来的字节流中的serialVersionUID与本地相应实体的serialVersionUID进行比较,发现不一致,因此会出现异常错误:

 

(2)指定serialVersionUID

这个情况就不展示了,不断你之前添加了多少个字段,或者进行更改,因为serialVersionUID唯一,因此反序列化都不会出现错误。

OK,这就是java中这个serialVersionUID的作用,其实就是给这个类添加一个身份ID,进行在序列化之前和之后进行版本的比对。上面这个其实也是一个面试常问的一个问题,再次凑巧给总结了一下,不过今天的主题不是讲这个serialVersionUID的,而是后面的那一串数字为什么总是无意义的?

二、为什么总是无意义的ID?

java序列化中的serialVersionUID后面我们通常是1L、或者是xxxL。这些数字有什么意义呢?为什么我们总是需要这些无意义的ID。带着这些问题我们一步一步来揭晓答案。

1、有意义的ID

有一些ID是有意义的,最常见的就是我们的身份证号,一共18位。分别代表着省市县等等。在通常情况下这个ID在全国内是惟一的。他就像是一个标识符一样,唯一地代表了我们。

 

标识符(identifier)就是一个可以唯一识别一个对象或者物体的名称,被识别的对象可能是一些想法、物理上可数的对象或者物理上的不可数物质。它的前缀 ID 经常被用来表示身份、鉴定过程或者标识符。

因此唯一性是ID的最大特点。好比是我们的身份证号码,整个中国你找不出第二个和你一样号码的人。现在我们知道了有意义的ID通常情况下是一个标识符,唯一地代表了这个物体。现在我们把目光转到无意义的ID。

2、无意义的ID

我们的java序列化id、数据库中的自增主键、消息队列、甚至于我们的TCP通信中都会使用到这个。无意义的真正含义其实是和我们要做的事无关,也就是说这个ID数字不应该和我们的业务逻辑产生联系。

大多数业务的主键都会使用整数,它的上限一般就是 2^64,如果这些位数都用来表示记录的 ID,那么在有生之年基本上是不可能被使用完的,但是一旦我们将业务信息加入 ID,就会让原本无意义的 ID 变得有意义从而影响它的唯一性。

java序列化的那个例子,你看到serialVersionUID==xxxL,应该想不到这一串数字和这个类有什么联系吧。而且一旦有联系就有可能会出现错误。那为什么无意义的ID是有用的呢?我们举一个例子:在分布式系统中有一个分布式的 ID 生成器,Snowflake 算法会为 64 个比特的整数赋予不同的信息:

范围 长度 作用
0-0 1 不使用
1-41 41 毫秒级时间戳
42-46 5 数据中心标识符
47-51 5 机器标识符
52-63 12 序列号

假设一台机器上一个时间单位最多只能生成 4096 个 ID,一旦超过了这个这个数量就有可能导致 ID 冲突或者乱序,从而失去其唯一性;这个算法中涉及的时间戳、数据中心标识符、机器标识符都没有办法解决唯一性的问题,哪怕这三者完全相等,此时仍然需要使用无其他意义的序列号来保证 ID 的唯一。

因此使用无意义 ID 的主要目的就是利用它的唯一性保证对象的标识符不会发生冲突,无意义 ID 的唯一作用就是保证唯一性,这能帮助我们避免业务字段可能存在潜在冲突的可能,这也提示我们想要使用联合字段构成主键时一定要深思熟虑。

3、总结

上面其实说了这么多,是想让各位有个稍微全面的了解。就像很多时候一句话讲完的事,非要BB半天。几句话总结:

对于有意义的ID,在特定场景下ID数字和业务逻辑有关,比如身份证号和每个人的唯一标识有关。

对于无意义的ID:这个ID数子一旦和业务逻辑产生联系,就有重复的可能,而且极其不安全。此时一个无意义的ID就有了唯一性。

 

不管有没有意义都是为了进行唯一标识,但是使用的场景不相同。

本文转载自微信公众号「愚公要移山」,可以通过以下二维码关注。转载本文请联系愚公要移山公众号。

 

责任编辑:武晓燕 来源: 愚公要移山
相关推荐

2020-07-27 15:24:22

戴尔

2023-03-09 08:23:07

序列化​接口方法

2011-06-01 15:18:43

Serializabl

2023-12-26 07:26:07

Java序列化反序列化机制

2015-09-14 09:34:37

Docker本地开发开发环境

2013-04-24 10:04:35

Windows 8.1

2009-03-26 22:54:33

IBM院士Sun

2018-03-19 10:20:23

Java序列化反序列化

2009-06-14 22:01:27

Java对象序列化反序列化

2018-06-25 17:05:09

区块链数字货币比特币

2024-09-03 08:17:59

2020-12-24 18:46:11

Java序列化编程语言

2011-06-01 15:05:02

序列化反序列化

2024-09-10 08:28:22

2020-08-12 08:35:34

华为阿里加班

2011-06-01 14:26:11

序列化

2023-09-12 07:24:07

Java序列化接口

2023-12-13 13:49:52

Python序列化模块

2010-03-19 15:54:21

Java Socket

2011-04-02 09:04:49

Java序列化
点赞
收藏

51CTO技术栈公众号