transient 关键字

本贴最后更新于 1833 天前,其中的信息可能已经时移世异

一、什么是 transient

简而言之,被 transient 标记的变量在序列化时会被忽略,当然没有序列化就没有反序列化

看看下面的例子来加深理解:

@Data class Employee implements Serializable { private String firstName; private String lastName; private transient String password; }
//序列化对象 try { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.txt")); Employee emp = new Employee(); emp.setFirstName("john"); emp.setLastName("jack"); emp.setPassword("password"); oos.writeObject(emp); oos.close(); } catch (Exception e) { System.out.println(e); }
// 反序列化 try { ObjectInputStream ooi = new ObjectInputStream(new FileInputStream("test.txt")); Employee readEmpInfo = (Employee) ooi.readObject(); System.out.println(readEmpInfo.getFirstName()); System.out.println(readEmpInfo.getLastName()); System.out.println(readEmpInfo.getPassword()); // john // jack // null ooi.close(); } catch (Exception e) { System.out.println(e); }

可以看到被 transient 修饰的属性反序列化为 null。

二、transient 应用场景

  1. 对象的某个属性的值,需要根据其他属性来计算的时候,应该把这个属性加上 transient

这种属性应该是每次都用程序计算,而不是用序列化来持久化。比如说基于时间戳的值,像年龄,或者需要保存一段时间,这段时间是某个时间戳和当前时间戳的时间段。这种情况就不应该将这种属性值序列化。

  1. 涉及隐私以及安全问题的属性不应该序列化。
  2. 本身就没有实现序列化接口的属性,如果被序列化会抛出 NotSerializableException 异常。
  3. 逻辑道理上不需要序列化。比如说类里面有个 Logger 对象,但是 Logger 只是用来写日志,不需要保留这个对象的状态。再比如线程对象,线程对象保留是某个时间点的线程状态,没有持久化的意义。

三、transientfinal 组合

一个属性同时被 transientfinal 修饰,如果这个属性属于基本数据类型和 String,那么序列化的时候 transient 会被会忽略掉。因为会被 JVM 认为是常量。

其他引用类型(包括基本数据类型的包装类),即使被 final 修饰,transient 也会生效

四、实际应用举例--HashMap

// HashMap中使用 transient标记的字段 transient Entry table[]; transient int size; transient int modCount; transient int hashSeed; private transient Set entrySet;

可以看出 HashMap 的 key-value 相关的存储都是用 transient 修饰的,也就是说序列化的时候不会被序列化。

private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { // Read in the threshold (ignored), loadfactor, and any hidden stuff s.defaultReadObject(); reinitialize(); if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new InvalidObjectException("Illegal load factor: " + loadFactor); s.readInt(); // Read and ignore number of buckets int mappings = s.readInt(); // Read number of mappings (size) if (mappings < 0) throw new InvalidObjectException("Illegal mappings count: " + mappings); else if (mappings > 0) { // (if zero, use defaults) // Size the table using given load factor only if within // range of 0.25...4.0 float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f); float fc = (float)mappings / lf + 1.0f; int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ? DEFAULT_INITIAL_CAPACITY : (fc >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : tableSizeFor((int)fc)); float ft = (float)cap * lf; threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ? (int)ft : Integer.MAX_VALUE); // Check Map.Entry[].class since it's the nearest public type to // what we're actually creating. SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, cap); @SuppressWarnings({"rawtypes","unchecked"}) Node<K,V>[] tab = (Node<K,V>[])new Node[cap]; table = tab; // Read the keys and values, and put the mappings in the HashMap for (int i = 0; i < mappings; i++) { @SuppressWarnings("unchecked") K key = (K) s.readObject(); @SuppressWarnings("unchecked") V value = (V) s.readObject(); putVal(hash(key), key, value, false, false); } } }

HashMap 反序列化后,会重新 hash 计算把 key-value 存进去。

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3194 引用 • 8214 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...