大家好,我是 V 哥,今天来聊一聊serialVersionUID常数、瞬时变量,这几个都是 Java 开发中比较基础的概念,但容易被大家所忽视,V 哥通过一篇文章来介绍清楚,让你无后顾之忧。先赞后看,家财万贯。

以下是关于serialVersionUID常数和瞬时变量的详细介绍:

serialVersionUID常数

  • 定义与作用serialVersionUID是Java中用于序列化和反序列化的一个重要概念。它是一个类的版本标识,用于确保在反序列化时,类的结构与序列化时的结构一致。如果类的serialVersionUID在序列化和反序列化之间发生了变化,JVM会认为这是两个不同的类,从而导致反序列化失败。
  • 原理:当一个类实现了java.io.Serializable接口,就可以被序列化和反序列化。在序列化过程中,JVM会根据类的结构和成员变量等信息生成一个serialVersionUID。在反序列化时,JVM会检查这个serialVersionUID是否与序列化时的一致。
  • 案例

    import java.io.Serializable;
    
    class Person implements Serializable {
      // 显式定义serialVersionUID
      private static final long serialVersionUID = 1L;
      private String name;
      private int age;
    
      public Person(String name, int age) {
          this.name = name;
          this.age = age;
      }
    }
    
    public class Main {
      public static void main(String[] args) {
          // 创建一个Person对象并进行序列化和反序列化操作
          Person person = new Person("Alice", 30);
          // 这里省略具体的序列化和反序列化代码
      }
    }

    在上述代码中,Person类实现了Serializable接口,并显式定义了serialVersionUID为1L。如果没有显式定义,Java会根据类的结构等自动生成一个serialVersionUID,但这样可能会导致在类的结构发生微小变化时,serialVersionUID也发生变化,从而导致反序列化问题。

瞬时变量

  • 定义与作用:在Java中,使用transient关键字修饰的变量被称为瞬时变量。瞬时变量在对象序列化时不会被保存到序列化后的字节流中,在反序列化时会被赋予默认值。
  • 原理:这是因为transient关键字告诉了Java的序列化机制,在进行序列化操作时,忽略这个变量,不将其状态保存到序列化数据中。
  • 案例

    import java.io.Serializable;
    
    class User implements Serializable {
      private String username;
      // 用transient修饰password,使其成为瞬时变量
      private transient String password;
    
      public User(String username, String password) {
          this.username = username;
          this.password = password;
      }
    }
    
    public class Main {
      public static void main(String[] args) {
          User user = new User("admin", "secret");
          // 这里省略具体的序列化和反序列化代码
          // 反序列化后,password将为null
      }
    }

    在上述代码中,User类中的password变量被声明为transient。当对User对象进行序列化和反序列化时,password变量的值不会被保存和恢复,反序列化后password的值将为null。这样可以避免敏感信息(如密码)在序列化过程中被泄露。

瞬时变量和瞬态变量有什么区别?

瞬时变量和瞬态变量在Java等编程语言中实际上是同一个概念,英文都是“transient variable”,它们没有本质区别,只是中文表述不同而已。以下从定义、使用场景、原理层面详细说明:

  • 定义角度:两者都是指被transient关键字修饰的变量。在对象序列化过程中,被transient修饰的变量不会被保存到序列化后的字节流中,在反序列化时会被赋予默认值。比如在Java中:

    import java.io.Serializable;
    
    class Data implements Serializable {
      private String normalVariable;
      // 这里无论是叫瞬时变量还是瞬态变量,都是指这个被transient修饰的变量
      private transient String transientVariable;
    
      public Data(String normal, String transientValue) {
          normalVariable = normal;
          transientVariable = transientValue;
      }
    }
  • 使用场景角度

    • 保护敏感信息:对于像密码这种敏感信息,将其定义为瞬态(瞬时)变量,就可以防止在对象序列化时密码被泄露。因为在反序列化后,密码字段会变为默认值,原密码不会被恢复。
    • 避免不必要的数据存储:有些变量的值在反序列化后可以通过其他方式重新计算或获取,将这类变量设置为瞬态(瞬时)变量,可以减少序列化数据的大小,提高序列化和反序列化的效率。
  • 原理角度:在Java的序列化机制中,当遇到transient修饰的变量时,序列化框架会跳过该变量的写入操作,在反序列化时也不会从字节流中读取数据来恢复该变量的值,而是直接赋予其默认值,这就是瞬态(瞬时)变量在底层的实现原理。

除了保护敏感信息,还有哪些场景适合使用瞬态变量?

除了保护敏感信息外,以下是一些适合使用瞬态变量的常见场景:

  • 缓存数据

    • 场景描述:在程序运行过程中,经常会在内存中缓存一些数据以提高访问性能。这些缓存数据通常是根据其他数据临时计算或加载而来,在序列化对象时,不需要保存这些缓存数据,因为在反序列化后可以重新计算或加载。
    • 代码示例
    import java.io.Serializable;
    
    class CacheData implements Serializable {
      private String originalData;
      // 用于缓存处理后的数据,不需要进行序列化
      private transient String processedCache;
    
      public CacheData(String data) {
          originalData = data;
      }
    
      public String getProcessedData() {
          if (processedCache == null) {
              // 模拟数据处理过程
              processedCache = originalData + " processed";
          }
          return processedCache;
      }
    }
  • 临时状态变量

    • 场景描述:当对象在运行期间会有一些临时的状态变量,这些变量仅在特定的方法调用或流程中起作用,并不属于对象的核心持久化状态。在序列化时,这些临时状态变量不需要被保存,以免在反序列化后造成状态混乱。
    • 代码示例
    import java.io.Serializable;
    
    class Transaction implements Serializable {
      private String transactionId;
      private String customerId;
      // 用于记录交易是否正在进行的临时状态,不需要持久化
      private transient boolean inProgress;
    
      public Transaction(String id, String customer) {
          transactionId = id;
          customerId = customer;
      }
    
      public void startTransaction() {
          inProgress = true;
          // 执行交易开始的逻辑
      }
    
      public void endTransaction() {
          inProgress = false;
          // 执行交易结束的逻辑
      }
    }
  • 资源引用

    • 场景描述:如果对象中包含对一些外部资源(如文件流、网络连接等)的引用,这些资源在序列化时无法被正确保存,而且在反序列化后也需要重新获取。将这些资源引用声明为瞬态变量,可以避免在序列化和反序列化过程中出现问题。
    • 代码示例
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.Serializable;
    
    class FileProcessor implements Serializable {
      private String filePath;
      // 文件输入流,不需要序列化,反序列化后重新打开
      private transient FileInputStream fileStream;
    
      public FileProcessor(String path) {
          filePath = path;
      }
    
      public void openFile() {
          try {
              fileStream = new FileInputStream(new File(filePath));
              // 执行文件读取逻辑
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
    
      public void closeFile() {
          try {
              if (fileStream!= null) {
                  fileStream.close();
              }
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
    }
  • 事件监听器或回调函数

    • 场景描述:在某些框架或应用中,对象可能会注册一些事件监听器或回调函数。这些监听器或回调函数通常是与当前运行时的上下文相关,不适合被序列化。将它们声明为瞬态变量,可以确保在序列化和反序列化过程中不会出现问题,并且在反序列化后可以重新注册监听器或设置回调函数。
    • 代码示例
    import java.io.Serializable;
    import java.util.ArrayList;
    import java.util.List;
    
    class EventEmitter implements Serializable {
      // 事件名称
      private String eventName;
      // 存储事件监听器的列表,不需要序列化
      private transient List<Runnable> eventListeners = new ArrayList<>();
    
      public EventEmitter(String name) {
          eventName = name;
      }
    
      public void addEventListener(Runnable listener) {
          eventListeners.add(listener);
      }
    
      public void emitEvent() {
          for (Runnable listener : eventListeners) {
              listener.run();
          }
      }
    }

最后

好了,这下彻底知道什么是serialVersionUID常数和瞬时变量了,使用起来会更加得心应手,关注威哥爱编程,全栈之路就你行。都看到这里了,动动用小手点个小赞鼓励一下V 哥呗,感谢老铁。


威哥爱编程
192 声望19 粉丝