1.transient 介绍

Java中的transient关键字,transient是短暂的意思。对于transient 修饰的成员变量,在类的实例对象的序列化处理过程中会被忽略。 因此,transient变量不会贯穿对象的序列化和反序列化,生命周期仅存于调用者的内存中而不会写到磁盘里进行持久化。
 
(1)序列化
      Java中对象的序列化指的是将对象转换成以字节序列的形式来表示,这些字节序列包含了对象的数据和信息,一个序列化后的对象可以被写到数据库或文件中,也可用于网络传输。一般地,当我们使用缓存cache(内存空间不够有可能会本地存储到硬盘)或远程调用rpc(网络传输)的时候,经常需要让实体类实现Serializable接口,目的就是为了让其可序列化。当然,序列化后的最终目的是为了反序列化,恢复成原先的Java对象实例。所以序列化后的字节序列都是可以恢复成Java对象的,这个过程就是反序列化。
 
(2)为什么要用transient关键字?
      在持久化对象时,对于一些特殊的数据成员(如用户的密码,银行卡号等),我们不想用序列化机制来保存它。为了在一个特定对象的一个成员变量上关闭序列化,可以在这个成员变量前加上关键字transient。
 
(3)transient的作用
      transient是Java语言的关键字,用来表示一个成员变量不是该对象序列化的一部分。当一个对象被序列化的时候,transient型变量的值不包括在序列化的结果中。而非transient型的变量是被包括进去的。   注意static修饰的静态变量天然就是不可序

 2. transient 使用总结

(1)一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法被访问。
 
(2) transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。
 
(3)一个静态变量不管是否被transient修饰,均不能被序列化(如果反序列化后类中static变量还有值,则值为当前JVM中对应static变量的值)。序列化保存的是对象状态,静态变量保存的是类状态,因此序列化并不保存静态变量。
 
3. 序列化及反序列化

  • 序列化
    是指将Java对象保存为二进制字节码的过程。
  • 反序列化
    将二进制字节码重新转成Java对象的过程。

(1)为什么序列化

  • 我们知道,一般Java对象的生命周期比Java虚拟机短,而实际的开发中,我们需要
    在Jvm停止后能够继续持有对象,这个时候就需要用到序列化技术将对象持久到磁盘或数据库。
  • 在多个项目进行RPC调用的,需要在网络上传输JavaBean对象。我们知道数据只能以二进制的
    形式才能在网络上进行传输。所以也需要用到序列化技术。

(2)序列化的底层原理

  1. 程序入口:

  1. writeObjectobj
  1. Student stu1 = new Student(1001, "jack", "play");
  2. Student stu2 = new Student(1002, "tom", "sleep");
  3. ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:\\stu.dat"));
  4. oos.writeObject(stu1);
  5. oos.writeObject(stu2);
  6. oos.close();
  1. 序列化
  • 在调用writeObject()方法之前,会先调用ObjectOutputStream的构造函数,生成
    一个ObjectOutputStream对象。
  1. public ObjectOutputStream(OutputStream out) throws IOException {
  2. verifySubclass();
  3. // bout是底层的数据字节容器
  4. bout = new BlockDataOutputStream(out);
  5. handles = new HandleTable(10, (float) 3.00);
  6. subs = new ReplaceTable(10, (float) 3.00);
  7. enableOverride = false;
  8. // 写入序列化文件头
  9. writeStreamHeader();
  10. // 设置文件缓存刷新配置
  11. bout.setBlockDataMode(true);
  12. if (extendedDebugInfo) {
  13. debugInfoStack = new DebugTraceInfoStack();
  14. } else {
  15. debugInfoStack = null;
  16. }
  17. }

  writeStreamHeader的方法内容如下:

  1. protected void writeStreamHeader() throws IOException {
  2. bout.writeShort(STREAM_MAGIC);
  3. bout.writeShort(STREAM_MAGIC);
  4. }

  

  3. 调用writeObject()方法进行具体的序列化写入

  1. public final void writeObject(Object obj) throws IOException {
  2. if (enableOverride) {
  3. writeObjectOverride(obj);
  4. return;
  5. }
  6. try {
  7. writeObject0(obj, false);
  8. } catch (IOException ex) {
  9. if (depth == 0) {
  10. writeFatalException(ex);
  11. }
  12. throw ex;
  13. }
  14. }

  

  • writeObject0的具体内容
  1. private void writeObject0(Object obj, boolean unshared)
  2. throws IOException
  3. {
  4. boolean oldMode = bout.setBlockDataMode(false);
  5. depth++;
  6. try {
  7. // handle previously written and non-replaceable objects
  8. int h;
  9. if ((obj = subs.lookup(obj)) == null) {
  10. writeNull();
  11. return;
  12. } else if (!unshared && (h = handles.lookup(obj)) != -1) {
  13. writeHandle(h);
  14. return;
  15. } else if (obj instanceof Class) {
  16. writeClass((Class) obj, unshared);
  17. return;
  18. } else if (obj instanceof ObjectStreamClass) {
  19. writeClassDesc((ObjectStreamClass) obj, unshared);
  20. return;
  21. }
  22.  
  23. // check for replacement object
  24. Object orig = obj;
  25. // 需要序列的对象的Class对象
  26. Class<?> cl = obj.getClass();
  27. ObjectStreamClass desc;
  28. for (;;) {
  29. // 提示:跳过检查string和数组
  30. // REMIND: skip this check for strings/arrays?
  31. Class<?> repCl;
  32. // 创建描述c1的ObjectStreamClass对象
  33. desc = ObjectStreamClass.lookup(cl, true);
  34. if (!desc.hasWriteReplaceMethod() ||
  35. (obj = desc.invokeWriteReplace(obj)) == null ||
  36. (repCl = obj.getClass()) == cl)
  37. {
  38. break;
  39. }
  40. cl = repCl;
  41. }
  42. if (enableReplace) {
  43. Object rep = replaceObject(obj);
  44. if (rep != obj && rep != null) {
  45. cl = rep.getClass();
  46. desc = ObjectStreamClass.lookup(cl, true);
  47. }
  48. obj = rep;
  49. }
  50.  
  51. // if object replaced, run through original checks a second time
  52. if (obj != orig) {
  53. subs.assign(orig, obj);
  54. if (obj == null) {
  55. writeNull();
  56. return;
  57. } else if (!unshared && (h = handles.lookup(obj)) != -1) {
  58. writeHandle(h);
  59. return;
  60. } else if (obj instanceof Class) {
  61. writeClass((Class) obj, unshared);
  62. return;
  63. } else if (obj instanceof ObjectStreamClass) {
  64. writeClassDesc((ObjectStreamClass) obj, unshared);
  65. return;
  66. }
  67. }
  68.  
  69. // remaining cases
  70. // 根据实际要写入的类型,进行不同的写入操作
  71. // 由此可以看出String、Array、Enum是直接写入操作的
  72. if (obj instanceof String) {
  73. writeString((String) obj, unshared);
  74. } else if (cl.isArray()) {
  75. writeArray(obj, desc, unshared);
  76. } else if (obj instanceof Enum) {
  77. writeEnum((Enum<?>) obj, desc, unshared);
  78. } else if (obj instanceof Serializable) {
  79. // 实现序列化接口的都会执行下面的方法
  80. // 从这里也可以看出Serializable是一个标记接口,其本身并没有什么意义
  81. writeOrdinaryObject(obj, desc, unshared);
  82. } else {
  83. if (extendedDebugInfo) {
  84. throw new NotSerializableException(
  85. cl.getName() + "\n" + debugInfoStack.toString());
  86. } else {
  87. throw new NotSerializableException(cl.getName());
  88. }
  89. }
  90. } finally {
  91. depth--;
  92. bout.setBlockDataMode(oldMode);
  93. }
  94. }

从上面可以看出主要做了两件事

  • 创建了ObjectStreamClass对象
  • 根据实际要写入的类型,进行不同的写入操作

  writeOrdinaryObject()

  

为什么说序列化并不安全

  因为序列化的对象数据转换为二进制,并且完全可逆。但是在RMI调用时

  所有private字段的数据都以明文二进制的形式出现在网络的套接字上,这显然是不安全的

  解决方案:

  • 1、 序列化Hook化(移位和复位)
  • 2、 序列数据加密和签名
  • 3、 利用transient的特性解决
  • 4、 打包和解包代理

4. transient 在序列化底层的应用

static和transient修饰的字段不能被序列化。

  1. private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {
  2. Field[] clFields = cl.getDeclaredFields();
  3. ArrayList<ObjectStreamField> list = new ArrayList<>();
  4. int mask = Modifier.STATIC | Modifier.TRANSIENT;
  5.  
  6. for (int i = 0; i < clFields.length; i++) {
  7. if ((clFields[i].getModifiers() & mask) == 0) {
  8. list.add(new ObjectStreamField(clFields[i], false, true));
  9. }
  10. }
  11. int size = list.size();
  12. return (size == 0) ? NO_FIELDS :
  13. list.toArray(new ObjectStreamField[size]);
  14. }

  

transient的作用及序列化的更多相关文章

  1. Transient的作用

    1:transient的作用及其使用方法 当一个对象实现类Serilizable接口,那么这个类就可以被序列化,java的这种序列化的模式为开发者提供了很多的便利. 然而在实际开发中,我们常常遇到这样 ...

  2. 简述serializable和transient关键字作用

    transient的作用及使用方法,官方解释为: Variables may be marked transient to indicate that they are not part of the ...

  3. java transient关键字作用,使用场景。

    java transient关键字作用,使用场景. 2016年08月31日 15:31:10 阅读数:4280 transient的作用及使用方法,官方解释为: Variables may be ma ...

  4. java transient关键字作用,使用场景

    transient的作用及使用方法,官方解释为: Variables may be marked transient to indicate that they are not part of the ...

  5. Java基础-Java中transient有什么用-序列化有那几种方式

    此文转载于知乎的一篇文章,看着写的非常全面,分享给大家. 先解释下什么是序列化 我们的对象并不只是存在内存中,还需要传输网络,或者保存起来下次再加载出来用,所以需要Java序列化技术. Java序列化 ...

  6. 【Java】java注解@Transient的作用, 配合JPA中时间段的查询

    java注解@Transient的作用 @Transient标注的属性,不会被ORM框架映射到数据库中. 用于数据库表字段和java实体属性不一致的时候,标注在属性上使用. 例如时间段的查询 查询 R ...

  7. 关于transient和static的序列化和反序列化

    做java开发有段时间了,最近没那么忙了,抽了点时间看了下java的源码 . 在读源码的时候看到了一个 transient 修饰的变量 ,字面意思是瞬变的.在以前的开发过程中也没用到过这个修饰语,查了 ...

  8. 使用transient关键字解决ehcache序列化错误

    使用Ehcache时发现个不起眼的小问题 在一个Model中有以下代码: public class MyModel implements Serializable { private static f ...

  9. 序列化,反序列化和transient关键字

    一.序列化和反序列化的概念 序列化:指把java对象转换为字节序列的过程. 反序列化:指把字节序列恢复为java对象的过程. 对象的序列化主要有两种用途: 1) 把对象的字节序列保存到硬盘上,通常存放 ...

随机推荐

  1. 谁再把IDEA的Project比作Eclipse的Workspace,我就跟谁急

    前言 你好,我是A哥(YourBatman). 有一个观点:若一个Java开发者能把IDEA玩得666,则技术一定不会差:但若玩不转IDEA(如不会设置.定制.解决日常问题.快捷键等等),那大概率水平 ...

  2. mysql、sql server、oracle大比较

    MYSQL 多个数据库多个用户形式(最好每个数据库对应一个用户),占用内存小,适用于所有平台,开源免费 客户端和命令窗口,都是由数据库决定内容-> use datebase; 组函数在selec ...

  3. linux系统权限管理拓展:特殊权限

    文件与目录权限设置不止读.写.执行这些,还有所谓的特殊权限,由于特殊权限会拥有一些"特权": 1 2 3 4 本章内容 SUID SGID SBIT 文件扩展权限ACL 1.SUI ...

  4. MacOS Big Sur开HiDPI

    我自己的环境: 开启hidpi的目的是为了让显示更加细腻,代价是缩小了显示范围. 自己在网上看了很多帖子,也尝试了几种,有些方法已经不再适合Big Sur系统了,所以本文提供一种可用的,在Big Su ...

  5. Centos8上搭建EMQ MQTT

    layout: post title: Centos8上搭建EMQ MQTT subtitle: 在阿里云Centos8搭建EMQ并配置接入 date: 2020-3-11 author: Dapen ...

  6. Inceptor常用SQL

    1.创建数据库 建一个数据库exchange_platform. DROP DATABASE IF EXISTS exchange_platform CASCADE; CREATE DATABASE ...

  7. [CCPC2019网络赛] 1008-Fishing Master(思维)

    >传送门< 题意:现在需要捕$n$条鱼并且将它们煮熟来吃.每条鱼要煮相应的时间才能吃(可以多煮一会),锅里每次只能煮一条鱼,捕一条鱼的时间是相同的,但是在捕鱼的时间内不能做其他事(比如换一 ...

  8. GPLT L2-024 部落 (并查集)

    N ≤ 104,输入如下数据如果没有路径压缩可能会超时. 10000 2 1 2 2 3 4 2 5 6 -- 2 9997 9998 2 9999 10000 2 9999 9997 -- 2 5 ...

  9. HDU6061 RXD and functions【NTT】

    \(RXD\ and\ functions\) Problem Description RXD has a polynomial function \(f(x)\), \(f(x)=\sum ^{n} ...

  10. Codeforces Round #479 (Div. 3) C. Less or Equal (排序,贪心)

    题意:有一个长度为\(n\)的序列,要求在\([1,10^9]\)中找一个\(x\),使得序列中恰好\(k\)个数满足\(\le x\).如果找不到\(x\),输出\(-1\). 题解:先对这个序列排 ...