Java序列化之serialVersionUID

爱被打了一巴掌 2022-09-07 06:14 254阅读 0赞

Java序列化之serialVersionUID

今天讲一讲Java对象中的serialVersionUID,先从序列化讲起。

什么是序列化

序列化,简单的说,就是将一个对象转化(编码)成可以传输的输出流(字节流)。而反序列化就是序列化的逆过程,将输入流转化(构建)成一个对象。

为什么要序列化

字节流可以用于网络传输和存储在磁盘,而对象需要转化成字节流才能在网络中传输和在磁盘上存储。
网络传输就好比打电话,声音是无法直接从电话的一端传到另一端,因此需要将声音转成电信号进行传播。
另一方面,Java对象是保存在JVM的堆内存中的,也就是说,如果JVM堆不存在了,那么对象也就跟着消失了,而序列化提供了可以把对象保存下来的方案。

serialVersionUID是个啥

说到序列化,serialVersionUID是个不得不谈的话题。serialVersionUID 是 Java 为每个序列化类(实现java.io.Serializable接口的类)产生的版本标识, 可用来保证在反序列时,发送方发送的和接受方接收的是可兼容的对象。 如果接收方接收的类的 serialVersionUID 与发送方发送的 serialVersionUID 不一致,进行反序列时会抛出 InvalidClassException。

怎么生成serialVersionUID

下载GenerateSerialVersionUID插件,就可以自动生产这个序列类的serialVersionUID了,具体过程可参考链接.
在这里插入图片描述

serialVersionUID是一成不变的吗

达咩!
serialVersionUID 是 Java 为每个序列化类产生的版本标识!!
Java序列化机制会根据编译的Class自动生成一个serialVersionUID作序列化版本比较用。如果Class文件的类名、方法名称发生改变,serialVersionUID就会改变。如果Class文件没有发生变化(增加空格,换行,增加注释等等),就算再编译多次,serialVersionUID也不会变化的。

  1. import java.io.Serializable;
  2. public class Person implements Serializable {
  3. // 原本的serialVersionUID
  4. private static final long serialVersionUID = 904XXXXXXXXXX662L;
  5. private int age;
  6. private String name;
  7. private String address;
  8. public Person(int age, String name, String address) {
  9. this.age = age;
  10. this.name = name;
  11. this.address = address;
  12. }
  13. public int getAge() {
  14. return age;
  15. }
  16. public void setAge(int age) {
  17. this.age = age;
  18. }
  19. public String getName() {
  20. return name;
  21. }
  22. public void setName(String name) {
  23. this.name = name;
  24. }
  25. public String getAddress() {
  26. return address;
  27. }
  28. public void setAddress(String address) {
  29. this.address = address;
  30. }
  31. }
  32. import java.io.Serializable;
  33. public class Person implements Serializable {
  34. // 加上toSting函数的serialVersionUID
  35. private static final long serialVersionUID = 841XXXXXXXXXXXXX884L;
  36. private int age;
  37. private String name;
  38. private String address;
  39. public Person(int age, String name, String address) {
  40. this.age = age;
  41. this.name = name;
  42. this.address = address;
  43. }
  44. public int getAge() {
  45. return age;
  46. }
  47. public void setAge(int age) {
  48. this.age = age;
  49. }
  50. public String getName() {
  51. return name;
  52. }
  53. public void setName(String name) {
  54. this.name = name;
  55. }
  56. public String getAddress() {
  57. return address;
  58. }
  59. public void setAddress(String address) {
  60. this.address = address;
  61. }
  62. @Override
  63. public String toString() {
  64. return "Person{" +
  65. "age=" + age +
  66. ", name='" + name + '\'' +
  67. ", address='" + address + '\'' +
  68. '}';
  69. }
  70. }

如果我手动改了serialVersionUID=11111111L会怎样?如果接收方接收的类的 serialVersionUID 与发送方发送的 serialVersionUID 不一致,进行反序列时会抛出 InvalidClassException。

  1. Exception in thread "main" java.io.InvalidClassException: SerializableStudy.Person; local class incompatible: stream classdesc serialVersionUID = 841XXXXXXXXXXXXX884, local class serialVersionUID = 11111111

序列化和反序列化

序列化要把对象写入输出流中,反序列化就是将输出流重新构建对象,二者为逆过程。当serialVersionUID改变时,一定要重新序列化,再进行反序列化。话不多说,放代码。以下是基于上面序列化类Person,做序列化和反序列化的演示:

  1. public class SerialTest {
  2. public static void main(String[] args) throws IOException {
  3. // 序列化
  4. Person p = new Person(0,"aaa","bbbbb");
  5. // 指定文件生成输出流
  6. FileOutputStream fos = new FileOutputStream("person.txt");
  7. // 将对象写出到指定的输出流
  8. ObjectOutputStream oos = new ObjectOutputStream(fos);
  9. // 将指定的对象写入ObjectOutputStream。
  10. oos.writeObject(p);
  11. // 刷新流
  12. oos.flush();
  13. oos.close();
  14. }
  15. }
  16. public class DeserialTest {
  17. public static void main(String[] args) throws IOException, ClassNotFoundException {
  18. // 反序列化
  19. // 根据指定文件生产输入流
  20. FileInputStream fis = new FileInputStream("person.txt");
  21. // 从指定的输入流中读回对象消息
  22. ObjectInputStream ois = new ObjectInputStream(fis);
  23. // 从ObjectInputStream读取一个对象
  24. Person p = (Person) ois.readObject();
  25. ois.close();
  26. System.out.println(p.toString());
  27. }
  28. }

输出的结果为
在这里插入图片描述
注意:
将指定对象写入ObjectOutputStream时,存储针对对象本身而不是针对类,没有实现序列化的类不会参与序列化和反序列化!!举个例子,如果Person中设置一个没有实现序列化的父类Home:

  1. public class Home {
  2. private String home;
  3. public String getHome() {
  4. return home;
  5. }
  6. public void setHome(String home) {
  7. this.home = home;
  8. }
  9. }
  10. public class Person extends Home implements Serializable {
  11. ......
  12. }

在序列化和反序列化的过程中,即使定义了Person对象的home属性,由于Home中没有实现序列类,因此对象的home属性不会进行序列化处理。
在这里插入图片描述

发表评论

表情:
评论列表 (有 0 条评论,254人围观)

还没有评论,来说两句吧...

相关阅读