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. Git轻松入门1:本地仓库篇

    什么是Git 版本控制系统 首先我们要明白,什么是版本控制系统(version control systems)? 版本控制系统,顾名思义,就是能控制文件处于哪个版本的系统. e.g. 你在博客园里编 ...

  2. Java——Number类

    在平时学习中,当我们需要使用数字的时候,通常使用内置数据类型,如byte,int,long,double等. int i =12; float a = 12.3; 在实际开发中,经常会遇到需要使用对象 ...

  3. H - Oil Skimming (挖石油)

    题意大概是,海上漂浮着一些符号为#的石油,你要去搜集他们,但是你的勺子呢能且只能挖到两个单元的石油.问你最多能挖多少勺.注意 不能挖到纯净的海水,不然石油会被纯净的海水稀释的. 二分匹配,计算出里边有 ...

  4. 【noi 2.6_9265】取数游戏(DP)

    题意:从自然数1到N中不取相邻2数地取走任意个数,问方案数. 解法:f[i][1]表示在前i个数中选了第i个的方案数,f[i][0]表示没有选第i个.f[i][1]=f[i-1][0];  f[i][ ...

  5. hdu 2089不要62 (数位dp)

    Problem Description 杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer). 杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来 ...

  6. Atcoder ABC155_C中有关c++ STL map的用法

    题目:https://atcoder.jp/contests/abc155/tasks/abc155_c 这道题的题意是给我们n个string,让我们统计每个string出现的次数,并输出次数最多的一 ...

  7. 牛客的两道dfs

    1.传送门:牛客13594-选择困难症 题意:给你k类物品,每类物品有a[i]个每个物品都有一个value,每类物品最多选一个,要求有多少种选法使得总value>m(没要求每类物品都必须选) 题 ...

  8. Mysql主从架构

    Mysql主从架构 1. 克隆虚拟机 克隆的虚拟机的网络适配,使得虚拟机可以进入局域网 vi /etc/sysconfig/network-scripts/ifcfg-eth0 删除 HWADDR所在 ...

  9. 4.安装fluentd用于收集集群内部应用日志

    作者 微信:tangy8080 电子邮箱:914661180@qq.com 更新时间:2019-06-13 11:02:14 星期四 欢迎您订阅和分享我的订阅号,订阅号内会不定期分享一些我自己学习过程 ...

  10. 操作系统 part1

    实验好多,人好累... 一.进程和线程 references: 进程三种基本状态 进程和线程的概念.区别和联系 进程和线程的主要区别(总结) 进程间通信IPC 1.进程 进程,是资源分配和调度的基本单 ...