transient关键字的作用及用法,java @transient

  transient关键字的作用及用法,java @transient

  关键词transient可能比较陌生,很少用到,但是关键词transient在java中有着不可或缺的作用!

  在学习java的过程中,transient关键字之所以很少,其实是和它的作用分不开的:transient关键字的主要作用是做一些被transient关键字修饰的成员属性变量不被序列化。其实也正是因为如此,学习过程中很少用到序列化操作,一般在实际开发中!至于连载,相信有很多小白童鞋一直发呆或者没有具体概念。本文将介绍它们。

  1、何谓序列化?

  说到序列化,随之而来的另一个概念是反序列化。小白童鞋别慌。记住序列化相当于记住反序列化。因为反序列化是序列化的逆过程,所以博主建议只记住序列化的概念,以免自己被搞糊涂。

  技术术语定义的序列化:

  宜春术语定义序列化:

  其实我总结的就是上面的结论。不懂的话,直接参考专业术语的定义就可以了。你明白了之后,只要记住我说的话就行了。如果你想不起来,请杀了我(我是玩M的天才)

  图了解序列化:

  什么?你不知道字节是什么?实际上,我已经在一篇关于IO stream的文章中介绍了序列化。放心吧,绝对很详细~看文章名字就知道了~

  如何解决写爬虫IP受阻的问题?立即使用。

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

  2、为何要序列化?

  在前一节中提到了序列化的概念。知道了概念,就要知道为什么要连载。

  在我说为什么要连载之前,博主,我先举个栗子:

  Java对象的序列化是指将对象转换成字节序列,字节序列包含对象的数据和信息。序列化对象可以被写到数据库或文件中也可用于网络传输。一般我们用缓存cache(内存空间不够的话可能会存在本地硬盘上)或者远程调用rpc(网络传输)的时候。

  栗子装饰要使用transient发展过程中的关键词:

  栗子装饰不需要transient发展过程中的关键词:

  不知道大家有没有想法为什么不应该连载?其实主要是为了节省存储空间。优化程序!

  当然,序列化的最终目的是反序列化并恢复到原始的Java对象。否则连载后你会怎么做?就像买菜一样,你用塑料袋包好,然后为了安全到家的方便,把塑料袋拿掉。所以所有序列化的字节序列都可以还原成Java对象,这个过程就是反序列化。

  3、序列化与transient的使用

  1,需要做序列化的对象的类,必须实现序列化接口:Java.lang.Serializable 接口(没有任何抽象方法的标志接口)。Java中的大部分类都实现了这个接口,比如String,Integer类等。不实现此接口的类将不会序列化或反序列化任何状态,并将引发NotSerializableException异常。

  2.底层会判断只有当前对象是Serializable的instanceof才允许序列化,Serializable的Java对象实例会判断。

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

  ==ObjectOutputStream:序列化由writeObject()方法完成==

  ==ObjectInputStream:通过readObject()方法反序列化==

  4.此类的所有属性都必须是可序列化的。如果有一个属性不需要是可序列化的,它必须被指示为瞬态的,并用transient关键字修饰。

  因为字节,所以必然涉及到流的操作,即对象流也叫序列化流ObjectOutputstream。下面就多种情况来分析一下序列化的操作代码!

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

  包装瞬态测试;

  导入Java . io . *;

  class info {//==========================注意,这里没有实现Serializable接口。

  私有字符串名称;

  私有临时字符串密码;

  public UserInfo(字符串名称,字符串psw) {

  this.name=name

  this.password=psw

  }

  @覆盖

  公共字符串toString() {

  返回“UserInfo{”

  name= name \

  ,password= password \

  };

  }

  }

  公共类瞬态演示{

  公共静态void main(String[] args) {

  UserInfo userInfo=新用户信息(老王,123);

  System.out.println(序列化之前信息: userInfo);

  尝试{

  对象输出流输出=新对象输出流(新文件输出流( userinfo。txt’);

  output.writeObject(新的UserInfo(老王,123));

  输出。close();

  } catch (IOException e) {

  e。printstacktrace();

  }

  }

  }运行结果

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

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

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

  3.3、普通序列化情况

  包装瞬态测试;

  导入Java。io。*;

  用户信息类实现可序列化的{ //第一步实现可序列化接口

  私有字符串名称;

  私有字符串密码;//都是普通属性==============================

  公共用户信息(字符串名称,字符串psw) {

  this.name=name

  this.password=psw

  }

  @覆盖

  公共字符串toString() {

  返回"用户信息{ "

  name= name \

  ,password= password \

  };

  }

  }

  公共类瞬态演示{

  公共静态void main(String[] args)引发ClassNotFoundException {

  UserInfo userInfo=新用户信息(程序员老王,123);

  System.out.println(序列化之前信息: userInfo);

  尝试{

  对象输出流输出=新对象输出流(新文件输出流( userinfo。txt’);//第二步开始序列化操作

  output.writeObject(新的UserInfo(程序员老王,123));

  输出。close();

  } catch (IOException e) {

  e。printstacktrace();

  }

  尝试{

  ObjectInputStream input=new ObjectInputStream(new file inputstream( userinfo。txt’));//第三步开始反序列化操作

  对象o=输入。read object();//ObjectInputStream的读取对象方法会抛出ClassNotFoundException

  System.out.println(序列化之后信息: o ;

  } catch (IOException e) {

  e。printstacktrace();

  }

  }

  }运行结果:

  序列化之前信息:UserInfo{name=程序员老王,密码=123}

  序列化之后信息:UserInfo{name=程序员老王,密码=123}3.4、transient序列化情况

  包装瞬态测试;

  导入Java。io。*;

  用户信息类实现可序列化的{ //第一步实现可序列化接口

  私有字符串名称;

  私有临时字符串密码;//特别注意:属性由短暂的关键字修饰===========

  公共用户信息(字符串名称,字符串psw) {

  this.name=name

  this.password=psw

  }

  @覆盖

  公共字符串toString() {

  返回"用户信息{ "

  name= name \

  ,password= password \

  };

  }

  }

  公共类瞬态演示{

  公共静态void main(String[] args)引发ClassNotFoundException {

  UserInfo userInfo=新用户信息(程序员老王,123);

  System.out.println(序列化之前信息: userInfo);

  尝试{

  对象输出流输出=新对象输出流(新文件输出流( userinfo。txt’);//第二步开始序列化操作

  output.writeObject(新的UserInfo(程序员老王,123));

  输出。close();

  } catch (IOException e) {

  e。printstacktrace();

  }

  尝试{

  ObjectInputStream input=new ObjectInputStream(new file inputstream( userinfo。txt’));//第三步开始反序列化操作

  对象o=输入。read object();//ObjectInputStream的读取对象方法会抛出ClassNotFoundException

  System.out.println(序列化之后信息: o ;

  } catch (IOException e) {

  e。printstacktrace();

  }

  }

  }运行结果:

  序列化之前信息:UserInfo{name=程序员老王,密码=123}

  序列化之后信息:UserInfo{name=程序员老王,密码=null}特别注意结果,添加短暂的修饰的属性值为默认值null!如果被短暂的修饰的属性为(同国际组织)国际组织类型,那它被序列化之后值一定是0,当然各位可以去试试,这能说明什么呢?说明被标记为短暂的的属性在对象被序列化的时候不会被保存(或者说变量不会持久化)

  3.5、static序列化情况

  包装瞬态测试;

  导入Java。io。*;

  用户信息类实现可序列化的{ //第一步实现可序列化接口

  私有字符串名称;

  私有静态字符串密码;//特别注意:属性由静电关键字修饰==============

  公共用户信息(字符串名称,字符串psw) {

  this.name=name

  this.password=psw

  }

  @覆盖

  公共字符串toString() {

  返回"用户信息{ "

  name= name \

  ,password= password \

  };

  }

  }

  公共类瞬态演示{

  公共静态void main(String[] args)引发ClassNotFoundException {

  UserInfo userInfo=新用户信息(程序员老王,123);

  System.out.println(序列化之前信息: userInfo);

  尝试{

  对象输出流输出=新对象输出流(新文件输出流( userinfo。txt’);//第二步开始序列化操作

  output.writeObject(新的UserInfo(程序员老王,123));

  输出。close();

  } catch (IOException e) {

  e。printstacktrace();

  }

  尝试{

  ObjectInputStream input=new ObjectInputStream(new file inputstream( userinfo。txt’));//第三步开始反序列化操作

  对象o=输入。read object();//ObjectInputStream的读取对象方法会抛出ClassNotFoundException

  System.out.println(序列化之后信息: o ;

  } catch (IOException e) {

  e。printstacktrace();

  }

  }

  }运行结果:

  序列化之前信息:UserInfo{name=程序员老王,密码=123}

  序列化之后信息:UserInfo{name=程序员老王,密码=123}这个时候,你就会错误的认为静电修饰的也被序列化了,其实不然,实际上这里很容易被搞晕!明明取出空(默认值)就可以说明不会被序列化,这里明明没有变成默认值,为何还要说静电不会被序列化呢?

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

  第一个程序:这是一个没有被静电修饰的名字属性程序:

  包装线;

  导入Java。io。文件输入流;

  导入Java。io。文件输出流;

  导入Java。io。objectinputstream

  导入Java。io。对象输出流;

  导入Java。io。可序列化;

  类用户信息实现可序列化{

  私有字符串名称;

  私有瞬态字符串西南太平洋(PacificSouthwest)

  公共用户信息(字符串名称,字符串psw) {

  this.name=name

  this.psw=psw

  }

  公共字符串getName() {

  返回名称;

  }

  公共void集合名称(字符串名){

  this.name=name

  }

  公共字符串getPsw() {

  返回西南太平洋(PacificSouthwest)

  }

  public void setPsw(String psw) {

  this.psw=psw

  }

  公共字符串toString() {

  return name= name ,psw= psw

  }

  }

  公共类测试瞬态{

  公共静态void main(String[] args) {

  UserInfo userInfo=新用户信息(程序员老过, 456);

  系统。出去。println(userInfo);

  尝试{

  //序列化,被设置为短暂的的属性没有被序列化

  对象输出流o=新对象输出流(新文件输出流( userinfo。txt’);

  哦。writeobject(userInfo);

  哦。close();

  } catch(异常e) {

  //TODO:处理异常

  e。printstacktrace();

  }

  尝试{

  //在反序列化之前更改name的值=========================注意这里的代码

  UserInfo.setName(程序员老改);

  //重新阅读内容

  ObjectInputStream in=new ObjectInputStream(new file inputstream( userinfo . txt );

  UserInfo read UserInfo=(UserInfo)in . read object();

  //读取后psw的内容为空

  system . out . println(read userinfo . tostring());

  } catch(异常e) {

  //TODO:处理异常

  e . printstacktrace();

  }

  }

  }运行结果:

  Name=程序员太老,psw=456

  Name=程序员太老,psw=null。从程序的运行结果可以看出,试图在反序列化之前为程序员更改name的值,结果不成功!

  第二个程序:这是一个由static修饰的名称属性程序:

  包装线;

  导入Java . io . file inputstream;

  导入Java . io . file output stream;

  导入Java . io . objectinputstream;

  导入Java . io . object output stream;

  导入Java . io . serializable;

  类UserInfo实现Serializable {

  private static final long serial version uid=996890129747019948 l;

  私有静态字符串名称;

  私有瞬态字符串psw

  public UserInfo(字符串名称,字符串psw) {

  this.name=name

  this.psw=psw

  }

  公共字符串getName() {

  返回名称;

  }

  public void setName(字符串名){

  this.name=name

  }

  公共字符串getPsw() {

  返回psw

  }

  public void setPsw(String psw) {

  this.psw=psw

  }

  公共字符串toString() {

  return name= name ,psw= psw

  }

  }

  公共类TestTransient {

  公共静态void main(String[] args) {

  Useruserinfo=newuserinfo(程序员太老了, 456 );

  system . out . println(userInfo);

  尝试{

  //序列化,设置为瞬态的属性不序列化

  object output stream o=new object output stream(new file output stream( userinfo . txt );

  o . writeobject(userInfo);

  o . close();

  } catch(异常e) {

  //TODO:处理异常

  e . printstacktrace();

  }

  尝试{

  //在反序列化之前更改name的值

  UserInfo.setName(程序员老改);

  //重新阅读内容

  ObjectInputStream in=new ObjectInputStream(new file inputstream( userinfo . txt );

  UserInfo read UserInfo=(UserInfo)in . read object();

  //读取后psw的内容为空

  system . out . println(read userinfo . tostring());

  } catch(异常e) {

  //TODO:处理异常

  e . printstacktrace();

  }

  }

  }运行结果:

  Name=程序员太老,psw=456

  Name=程序员老改,psw=null。从程序的运行结果可以看出,在反序列化之前,尝试将name的值改为程序员老改,结果成功!现在对比两个方案清楚了吗?

  static关键字修饰的成员属性比加载到内存中的非静态成员属性好,static比对象进入内存好。由static修饰的成员变量不能序列化,但所有序列化的对象。静态变量不是对象状态的一部分,所以它们不参与序列化。因此,将静态变量声明为瞬态变量是没有用的。因此,反序列化类中静态变量名的值实际上是当前JVM中对应静态变量的值,这个值在JVM中,没有反序列化。

  如果你还是没有理解清楚静态关键词,可以参考这篇文章,应该算是不错的:深入理解静态关键词。

  3.6、final序列化情况

  至于final关键字,final变量会通过值直接参与序列化。至于代码程序,我就不再贴了。可以试着用最后的装修来验证一下!

  重点是final和transient可以同时修改同一个变量,结果是一样的,对transient没有影响。在这里,我主要提一下。希望你在以后的发展中遇到这些情况不要迷茫!

  4、java类中serialVersionUID作用

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

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

  5、transient关键字小结

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

  第三点要注意:反序列化类中静态变量的值实际上是当前JVM中对应静态变量的值。这个值在JVM中,并且没有被反序列化。

  结语:不序列化,因为是用transient关键字修改的,优点是节省存储空间。优化程序!然后由transient修饰的字段将被重新计算和初始化!

  本文来自java入门专栏,欢迎学习!以上是深入学习java transient关键字的详细内容。更多请关注我们的其他相关文章!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: