前言

说实话学了一段时间java的朋友对于transient这个关键字依旧很陌生基本没怎么用过,但是transient关键字在java中却起到了不可或缺的地位!如果要说讲到,我觉得最可能出现的地方是IO流中对象流(也叫序列化流)的时候会讲到!

相信很多人都是直到自己碰到才会关心这个关键字,记得博主第一次碰到transient关键字是在阅读JDK源码的时候。在学习java的过程中transient关键字少见的原因其实离不开它的作用:transient关键字的主要作用就是让某些被transient关键字修饰的成员属性变量不被序列化。实际上也正是因此,在学习过程中很少用得上序列化操作,一般都是在实际开发中!至于序列化,相信有很多小白童鞋一直迷迷糊糊或者没有具体的概念,这都不是事,下面博主会很清楚的让你记住啥是序列化,保证你这辈子忘不了(貌似有点夸张,有点装b,感觉要被打)

@

1、何谓序列化?

说起序列化,随之而来的另一个概念就是反序列化,小白童鞋不要慌,记住了序列化就相当于记住了反序列化,因为反序列化就是序列化反过来,所以博主建议只记住序列化概念即可,省的搞晕自己。

专业术语定义的序列化:

Java提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。

宜春的术语定义序列化:

序列化: 字节 ——> 对象

其实,我总结的就是上面的结论,如果不理解,直接参照专业术语的定义,理解之后就记住我的话就行了,记不住,请打死我(我踢m简直就是个天才)

图理解序列化:



啥?你不懂啥是字节?其实,我在一篇IO流的文章里就已经介绍了序列化,放心,绝对特别详细光看文章名字就知道了

史上最骚最全最详细的IO流教程,小白都能看懂!

2、为何要序列化?

从上一节提到序列化的概念,知道概念之后,我们就必须要知道 为何要序列化了。

讲为何要序列化原因之前,博主我举个栗子:

就像你去街上买菜,一般操作都是用塑料袋给包装起来,直到回家要做菜的时候就把菜给拿出来。而这一系列操作就像极了序列化和反序列化!

Java中对象的序列化指的是将对象转换成以字节序列的形式来表示,这些字节序列包含了对象的数据和信息,一个序列化后的对象 可以被写到数据库或文件中,也可用于 网络传输,一般当我们使用 缓存cache(内存空间不够有可能会本地存储到硬盘)或 远程调用rpc(网络传输)的时候,经常需要让我们的实体类实现Serializable接口,目的就是为了让其可序列化。

  • 在开发过程中要使用transient关键字修饰的栗子:

如果一个用户有一些密码等信息,为了安全起见,不希望在网络操作中被传输,这些信息对应的变量就可以加上transient关键字。换句话说,这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。

  • 在开发过程中不需要transient关键字修饰的栗子:

1、类中的字段值可以根据其它字段推导出来。

2、看具体业务需求,哪些字段不想被序列化;

不知道各位有木有想过为什么要不被序列化呢?其实主要是为了节省存储空间。优化程序!

PS:记得之前看HashMap源码的时候,发现有个字段是用transient修饰的,我觉得还是有道理的,确实没必要对这个modCount字段进行序列化,因为没有意义,modCount主要用于判断HashMap是否被修改(像put、remove操作的时候,modCount都会自增),对于这种变量,一开始可以为任何值,0当然也是可以(new出来、反序列化出来、或者克隆clone出来的时候都是为0的),没必要持久化其值。

当然,序列化后的最终目的是为了反序列化,恢复成原先的Java对象,要不然序列化后干嘛呢,就像买菜一样,用塑料袋包裹最后还是为了方便安全到家再去掉塑料袋,所以序列化后的字节序列都是可以恢复成Java对象的,这个过程就是反序列化。

3、序列化与transient的使用

 1、需要做序列化的对象的类,必须实现序列化接口:Java.lang.Serializable 接口(一个标志接口,没有任何抽象方法),Java 中大多数类都实现了该接口,比如:StringInteger类等,不实现此接口的类将不会使任何状态序列化或反序列化,会抛NotSerializableException异常 。

  2、底层会判断,如果当前对象是 Serializable 的实例,才允许做序列化,Java对象 instanceof Serializable 来判断。

  3、在 Java 中使用对象流ObjectOutputStream来完成序列化以及ObjectInputStream流反序列化   

  1. ObjectOutputStream:通过 writeObject()方法做序列化操作 

  2. ObjectInputStream:通过 readObject() 方法做反序列化操作

4、该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient 关键字修饰。



由于字节嘛所以肯定要涉及流的操作,也就是对象流也叫序列化流ObjectOutputstream,下面进行多种情况分析序列化的操作代码!

在这里,我真的强烈建议看宜春博客的读者朋友,请试着去敲,切记一眼带过或者复制过去运行就完事了,特别是小白童鞋,相信我!你一定会有不一样的收获。千万不要觉得浪费时间,有时候慢就是快,宜春亲身体会!

3.1、没有实现Serializable接口进行序列化情况

package TransientTest;
import java.io.*; class UserInfo { //================================注意这里没有实现Serializable接口
private String name;
private transient String password; public UserInfo(String name,String psw) {
this.name = name;
this.password=psw;
} @Override
public String toString() {
return "UserInfo{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
} public class TransientDemo {
public static void main(String[] args) { UserInfo userInfo=new UserInfo("老王","123");
System.out.println("序列化之前信息:"+userInfo); try {
ObjectOutputStream output=new ObjectOutputStream(new FileOutputStream("userinfo.txt"));
output.writeObject(new UserInfo("老王","123"));
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

运行结果

3.2、实现Serializable接口序列化情况

当我们加上实现Serializable接口再运行会发现,项目中出现的userinfo.txt文件内容是这样的:



其实这都不是重点,重点是序列化操作成功了!

3.3、普通序列化情况

package TransientTest;
import java.io.*; class UserInfo implements Serializable{ //第一步实现Serializable接口
private String name;
private String password;//都是普通属性============================== public UserInfo(String name,String psw) {
this.name = name;
this.password=psw;
} @Override
public String toString() {
return "UserInfo{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
} public class TransientDemo {
public static void main(String[] args) throws ClassNotFoundException { UserInfo userInfo=new UserInfo("程序员老王","123");
System.out.println("序列化之前信息:"+userInfo); try {
ObjectOutputStream output=new ObjectOutputStream(new FileOutputStream("userinfo.txt")); //第二步开始序列化操作
output.writeObject(new UserInfo("程序员老王","123"));
output.close();
} catch (IOException e) {
e.printStackTrace();
} try {
ObjectInputStream input=new ObjectInputStream(new FileInputStream("userinfo.txt"));//第三步开始反序列化操作
Object o = input.readObject();//ObjectInputStream的readObject方法会抛出ClassNotFoundException
System.out.println("序列化之后信息:"+o);
} catch (IOException e) {
e.printStackTrace();
}
}
}

运行结果:

序列化之前信息:UserInfo{name='程序员老王', password='123'}
序列化之后信息:UserInfo{name='程序员老王', password='123'}

3.4、transient序列化情况

package TransientTest;
import java.io.*; class UserInfo implements Serializable{ //第一步实现Serializable接口
private String name;
private transient String password; //特别注意:属性由transient关键字修饰=========== public UserInfo(String name,String psw) {
this.name = name;
this.password=psw;
} @Override
public String toString() {
return "UserInfo{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
} public class TransientDemo {
public static void main(String[] args) throws ClassNotFoundException { UserInfo userInfo=new UserInfo("程序员老王","123");
System.out.println("序列化之前信息:"+userInfo); try {
ObjectOutputStream output=new ObjectOutputStream(new FileOutputStream("userinfo.txt")); //第二步开始序列化操作
output.writeObject(new UserInfo("程序员老王","123"));
output.close();
} catch (IOException e) {
e.printStackTrace();
} try {
ObjectInputStream input=new ObjectInputStream(new FileInputStream("userinfo.txt"));//第三步开始反序列化操作
Object o = input.readObject();//ObjectInputStream的readObject方法会抛出ClassNotFoundException
System.out.println("序列化之后信息:"+o);
} catch (IOException e) {
e.printStackTrace();
}
}
}

运行结果:

序列化之前信息:UserInfo{name='程序员老王', password='123'}
序列化之后信息:UserInfo{name='程序员老王', password='null'}

特别注意结果,添加transient修饰的属性值为默认值null!如果被transient修饰的属性为int类型,那它被序列化之后值一定是0,当然各位可以去试试,这能说明什么呢?说明被标记为transient的属性在对象被序列化的时候不会被保存(或者说变量不会持久化)

3.5、static序列化情况

package TransientTest;
import java.io.*; class UserInfo implements Serializable{ //第一步实现Serializable接口
private String name;
private static String password; //特别注意:属性由static关键字修饰============== public UserInfo(String name, String psw) {
this.name = name;
this.password=psw;
} @Override
public String toString() {
return "UserInfo{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
} public class TransientDemo {
public static void main(String[] args) throws ClassNotFoundException { UserInfo userInfo=new UserInfo("程序员老王","123");
System.out.println("序列化之前信息:"+userInfo); try {
ObjectOutputStream output=new ObjectOutputStream(new FileOutputStream("userinfo.txt")); //第二步开始序列化操作
output.writeObject(new UserInfo("程序员老王","123"));
output.close();
} catch (IOException e) {
e.printStackTrace();
} try {
ObjectInputStream input=new ObjectInputStream(new FileInputStream("userinfo.txt"));//第三步开始反序列化操作
Object o = input.readObject();//ObjectInputStream的readObject方法会抛出ClassNotFoundException
System.out.println("序列化之后信息:"+o);
} catch (IOException e) {
e.printStackTrace();
}
}
}

运行结果:

序列化之前信息:UserInfo{name='程序员老王', password='123'}
序列化之后信息:UserInfo{name='程序员老王', password='123'}

这个时候,你就会错误的认为static修饰的也被序列化了,其实不然,实际上这里很容易被搞晕!明明取出null(默认值)就可以说明不会被序列化,这里明明没有变成默认值,为何还要说static不会被序列化呢?

实际上,反序列化后类中static型变量name的值实际上是当前JVM中对应static变量的值,这个值是JVM中的并不是反序列化得出的。也就是说被static修饰的变量并没有参与序列化!但是咱也不能口说无凭啊,是的,那我们就来看两个程序对比一下就明白了!

第一个程序:这是一个没有被static修饰的name属性程序:

package Thread;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable; class UserInfo implements Serializable {
private String name;
private transient String psw; public UserInfo(String name, String psw) {
this.name = name;
this.psw = psw;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getPsw() {
return psw;
} public void setPsw(String psw) {
this.psw = psw;
} public String toString() {
return "name=" + name + ", psw=" + psw;
}
}
public class TestTransient {
public static void main(String[] args) {
UserInfo userInfo = new UserInfo("程序员老过", "456");
System.out.println(userInfo);
try {
// 序列化,被设置为transient的属性没有被序列化
ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("UserInfo.txt"));
o.writeObject(userInfo);
o.close();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
try {
//在反序列化之前改变name的值 =================================注意这里的代码
userInfo.setName("程序员老改");
// 重新读取内容
ObjectInputStream in = new ObjectInputStream(new FileInputStream("UserInfo.txt"));
UserInfo readUserInfo = (UserInfo) in.readObject();
//读取后psw的内容为null
System.out.println(readUserInfo.toString());
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}

运行结果:

name=程序员老过, psw=456
name=程序员老过, psw=null

从程序运行结果中可以看出,在反序列化之前试着改变name的值为程序员老改,结果是没有成功的!

第二个程序:这是一个被static修饰的name属性程序:

package Thread;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable; class UserInfo implements Serializable {
private static final long serialVersionUID = 996890129747019948L;
private static String name;
private transient String psw; public UserInfo(String name, String psw) {
this.name = name;
this.psw = psw;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getPsw() {
return psw;
} public void setPsw(String psw) {
this.psw = psw;
} public String toString() {
return "name=" + name + ", psw=" + psw;
}
}
public class TestTransient {
public static void main(String[] args) {
UserInfo userInfo = new UserInfo("程序员老过", "456");
System.out.println(userInfo);
try {
// 序列化,被设置为transient的属性没有被序列化
ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("UserInfo.txt"));
o.writeObject(userInfo);
o.close();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
try {
//在反序列化之前改变name的值
userInfo.setName("程序员老改");
// 重新读取内容
ObjectInputStream in = new ObjectInputStream(new FileInputStream("UserInfo.txt"));
UserInfo readUserInfo = (UserInfo) in.readObject();
//读取后psw的内容为null
System.out.println(readUserInfo.toString());
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}

运行结果:

name=程序员老过, psw=456
name=程序员老改, psw=null

从程序运行结果中可以看出,在反序列化之前试着改变name的值为程序员老改,结果是成功的!现在对比一下两个程序是不是就很清晰了?

static关键字修饰的成员属性优于非静态成员属性加载到内存中,同时静态也优于对象进入到内存中,被static修饰的成员变量不能被序列化,序列化的都是对象,静态变量不是对象状态的一部分,因此它不参与序列化。所以将静态变量声明为transient变量是没有用处的。因此,反序列化后类中static型变量name的值实际上是当前JVM中对应static变量的值,这个值是JVM中的并不是反序列化得出的。

如果对static关键字还是不太清楚理解的童鞋可以参考这篇文章,应该算是不错的:深入理解static关键字

3.6、final序列化情况

对于final关键字来讲,final变量将直接通过值参与序列化,至于代码程序我就不再贴出来了,大家可以试着用final修饰验证一下!

主要注意的是final 和transient可以同时修饰同一个变量,结果也是一样的,对transient没有影响,这里主要提一下,希望各位以后在开发中遇到这些情况不会满头雾水!

4、java类中serialVersionUID作用

既然提到了transient关键字就不得不提到序列化,既然提到了序列化,就不得不提到serialVersionUID了,它是啥呢?基本上有序列化就会存在这个serialVersionUID。



serialVersionUID适用于Java的序列化机制。简单来说,Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException,在开发中有时候可写可不写,建议最好还是写上比较好。

5、transient关键字小结

1、变量被transient修饰,变量将不会被序列化

2、transient关键字只能修饰变量,而不能修饰方法和类。

3、被static关键字修饰的变量不参与序列化,一个静态static变量不管是否被transient修饰,均不能被序列化。

4、final变量值参与序列化,final transient同时修饰变量,final不会影响transient,一样不会参与序列化

第二点需要注意的是:本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口

第三点需要注意的是:反序列化后类中static型变量的值实际上是当前JVM中对应static变量的值,这个值是JVM中的并不是反序列化得出的。

结语:被transient关键字修饰导致不被序列化,其优点是可以节省存储空间。优化程序!随之而来的是会导致被transient修饰的字段会重新计算,初始化!

如果本文对你有一点点帮助,那么请点个赞呗,谢谢~

若有不足或者不正之处,欢迎指正批评,感激不尽!如果有疑问欢迎留言,绝对第一时间回复!

最后,欢迎各位关注宜春的公众号,一起探讨技术,向往技术,追求技术,说好了来了就是盆友喔...

java中的transient关键字详解的更多相关文章

  1. java中的static关键字详解

    static对于我们这些初学者在编写代码和阅读代码是一个难以理解的关键字,也是大量公司面试题最喜欢考的之一.下面我就来就先讲述一下static关键字的用法和我们初学者容易误解的地方. static关键 ...

  2. Java中的main()方法详解

    在Java中,main()方法是Java应用程序的入口方法,也就是说,程序在运行的时候,第一个执行的方法就是main()方法,这个方法和其他的方法有很大的不同,比如方法的名字必须是main,方法必须是 ...

  3. Java面试题04-final关键字详解

    Java面试题04-final关键字详解 本篇博客将会讨论java中final关键字的含义,以及final用在什么地方,感觉看书总会有一些模糊,而且解释的不是很清楚,在此做个总结,以备准备面试的时候查 ...

  4. 关于Java中的transient关键字

    Java中的transient关键字是在序列化时候用的,如果用transient修饰变量,那么该变量不会被序列化. 下面的例子中创建了一个Student类,有三个成员变量:id,name,age.ag ...

  5. java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET

    java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET 亲,“社区之星”已经一周岁了!      社区福利快来领取免费参加MDCC大会机会哦    Tag功能介绍—我们 ...

  6. Java I/O : Java中的进制详解

    作者:李强强 上一篇,泥瓦匠基础地讲了下Java I/O : Bit Operation 位运算.这一讲,泥瓦匠带你走进Java中的进制详解. 一.引子 在Java世界里,99%的工作都是处理这高层. ...

  7. JAVA中的GC机制详解

    优秀Java程序员必须了解的GC工作原理 一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只 ...

  8. java中list和map详解

    一.概叙 List , Set, Map都是接口,前两个继承至Collection接口,Map为独立接口, List下有ArrayList,Vector,LinkedList Set下有HashSet ...

  9. Java中的transient关键字

    转载于:[lfsf802](http://blog.csdn.net/lfsf802/article/details/43239663) 关键字介绍 一个对象只要实现了Serilizable接口,这个 ...

随机推荐

  1. 【原】iOS开发进阶(唐巧)读书笔记(二)

    第三部分:iOS开发底层原理 1.Objective-C对象模型 1.1 isa指针 NSObject.h部分代码: NS_ROOT_CLASS @interface NSObject <NSO ...

  2. 包管理工具-yum

    yum介绍 yum(全称为 Yellow dog Updater, Modified)是一个在 Fedora和 RedHat 以及 CentOS 中的 Shell 前端软件包管理器.基于 RPM 包管 ...

  3. vue.js 使用 vue-router 修改页面标题

    module.exports = { name: 'myComponent', data: {} route{ data: function(){ document.title = "页面标 ...

  4. OptimalSolution(4)--字符串问题(2)进阶

    一.将整数字符串转成整数值 二.判断字符数组中是否所有的字符都只出现过一次 三.在有序但含有空的数组中查找字符串 四.数组中两个字符串的最小距离 五.添加最少字符使字符串整体都是回文字符串 六.括号字 ...

  5. Java基础(六)判断两个对象相等:equals、hashcode、toString方法

    1.equal方法 Object类中的equal方法用于检测一个对象是否等于另外一个对象.在Object类中,这个方法将判断两个对象是否具有相同的引用.如果两个对象具有相同的引用,它们一定是相等的.然 ...

  6. js自增图片切换

    使用js自增进行图片的切换 <!DOCTYPE html> <html lang="zh"> <head> <meta charset=& ...

  7. 随机数产生器:Random.Next

    Random类是一个产生伪随机数字的类,它的构造函数有两种,一个是直接New Random(),另外一个是New Random(Int32),前者是根据触发那刻的系统时间做为种子,来产生一个随机数字, ...

  8. Application,Session,Cookie,ViewState,Cache对象用法、作用域的区别

    1.Application:用于保存所有用户共用的数据信息.在Asp.Net中类似的配置数据最好保存在Web.config文件中.如果使用Application对象,一个需要考虑的问题是任何写操作都要 ...

  9. CSS3 变形、过渡、动画、关联属性浅析

    一.变形 transform:可以对元素对象进行旋转rotate.缩放scale.移动translate.倾斜skew.矩阵变形matrix.示例: transform: rotate(90deg) ...

  10. shell 队列实现线程并发控制

    需求:并发检测1000台web服务器状态(或者并发为1000台web服务器分发文件等)如何用shell实现? 方案一:(这应该是大多数人都第一时间想到的方法吧) 思路:一个for循环1000次,顺序执 ...