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的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。