java关键字之transient使用 偏执的太偏执、 2022-11-29 11:45 168阅读 0赞 ### java关键字之transient使用 ### * * 前言 * 一、初识transient关键字 * * User.java类 * TestTransient .java测试类 * 运行结果 * 二、深入分析transient关键字 * * 1、transient底层实现原理是什么? * 2、被transient关键字修饰过得变量真的不能被序列化嘛? * * User.java类 * TestTransient.java测试类 * 运行结果 * 3、静态变量能被序列化吗?没被transient关键字修饰之后呢? * * User.java类 * TestTransient.java测试类 * 运行结果 * 三、transient关键字总结 ## 前言 ## > 近期在查看jdk源码时经常看到有使用这个关键字,修饰成员变量,所以查阅了一下其用处,结合样例代码,详细分析。 ## 一、初识transient关键字 ## > 其实这个关键字的作用很好理解,就是简单的一句话:将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会被序列化,但在类实现Externalizable接口,并重写writeExternal和readExternal方法后,transient修改后的变量,也可以被序列化。 > 概念也很好理解,下面使用代码去验证一下: ### User.java类 ### public class User implements Serializable { private static final long serialVersionUID = 123456L; private transient int age; private int num; private String name; public int getNum() { return num; } public void setNum(int num) { this.num = num; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User[age:" + age + ",num:" + num + ",name:"+name + "]"; } } ### TestTransient .java测试类 ### public class TestTransient { public static void main(String[] args) throws IOException, ClassNotFoundException { serializeUser(); deSerializeUser(); } private static void serializeUser() throws IOException { User user = new User(); user.setName("java的transient关键字"); user.setAge(20); user.setNum(1); File file = new File("transient.txt"); if(!file.exists()){ file.createNewFile(); } ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file)); oos.writeObject(user); oos.close(); System.out.println("添加了transient关键字反序列化:user= " + user); } private static void deSerializeUser() throws IOException, ClassNotFoundException { File file = new File("transient.txt"); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)); User newUser = (User) ois.readObject(); System.out.println("添加了transient关键字反序列化:newUser= " + newUser); } } > 从上面可以看出,在序列化SerializeUser方法中,首先创建一个序列化user类,然后将其写入到transient.txt文件中。在反序列化DeSerializeUser方法中,首先创建一个File对象,然后读取transient.txt文件中的数据。 > 这就是序列化和反序列化的基本实现,而且我们看一下结果,也就是被transient关键字修饰的age属性是否被序列化。 ### 运行结果 ### ![在这里插入图片描述][20200820151540846.png_pic_center] > 从上面的这张图可以看出,age属性变为了0,说明被transient关键字修饰之后没有被序列化。 ## 二、深入分析transient关键字 ## > 为了更加深入的去分析transient关键字,我们需要带着几个问题去解读: > (1)transient底层实现的原理是什么? > (2)被transient关键字修饰过得变量真的不能被序列化嘛? > (3)静态变量能被序列化吗?被transient关键字修饰之后呢? ### 1、transient底层实现原理是什么? ### > java的serialization提供了一个非常棒的存储对象状态的机制,说白了serialization就是把对象的状态存储到硬盘上 去,等需要的时候就可以再把它读出来使用。有些时候像银行卡号这些字段是不希望在网络上传输的,transient的作用就是把这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化,意思是transient修饰的age字段,他的生命周期仅仅在内存中,不会被写到磁盘中。 ### 2、被transient关键字修饰过得变量真的不能被序列化嘛? ### 想要解决这个问题,首先还要再重提一下对象的序列化方式: Java序列化提供两种方式。 一种是实现Serializable接口。 另一种是实现Externalizable接口。 需要重写writeExternal和readExternal方法, 它的效率比Serializable高一些, 并且可以决定哪些属性需要序列化(即使是transient修饰的), 但是对大量对象,或者重复对象,则效率低。 从上面的这两种序列化方式,我想你已经看到了, 使用Exteranlizable接口实现序列化时, 我们自己指定那些属性是需要序列化的, 即使是transient修饰的。下面就验证一下 首先我们定义User类:这个类是被Externalizable接口修饰的 #### User.java类 #### public class User implements Externalizable { //private static final long serialVersionUID = 123456L; private transient int age; private int num; private String name; public int getNum() { return num; } public void setNum(int num) { this.num = num; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User[age:" + age + ",num:" + num + ",name:"+name + "]"; } @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(age); out.writeInt(num); out.writeObject(name); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { age = in.readInt(); num = in.readInt(); name = (String)in.readObject(); } } #### TestTransient.java测试类 #### public class TestTransient { public static void main(String[] args) throws IOException, ClassNotFoundException { serializeUser(); deSerializeUser(); } private static void serializeUser() throws IOException { User user = new User(); user.setName("java的transient关键字"); user.setAge(20); user.setNum(1); File file = new File("transient.txt"); if(!file.exists()){ file.createNewFile(); } ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file)); oos.writeObject(user); oos.close(); System.out.println("添加了transient关键字反序列化:user= " + user); } private static void deSerializeUser() throws IOException, ClassNotFoundException { File file = new File("transient.txt"); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)); User newUser = (User) ois.readObject(); System.out.println("添加了transient关键字反序列化:newUser= " + newUser); } } #### 运行结果 #### ![在这里插入图片描述][20200820152642326.png_pic_center] > 结果基本上验证了我们的猜想,也就是说,实现了Externalizable接口,哪一个属性被序列化使我们手动去指定的,即使是transient关键字修饰也不起作用。 > 另外如果存在多个相同类型的属性序列化、反序列化时,按照赋值的序列化顺序,再进行反序列化,就好了,比如上面有两个int类型的变量。 ### 3、静态变量能被序列化吗?没被transient关键字修饰之后呢? ### > 这个我可以提前先告诉结果,静态变量是不会被序列化的,即使没有transient关键字修饰。下面去验证一下,然后再解释原因。 > 首先,在User类中对age属性添加transient关键字和static关键字修饰。 #### User.java类 #### public class User implements Serializable { private static final long serialVersionUID = 123456L; private static transient int age; private String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User[age:" + age + ",name:"+name + "]"; } } #### TestTransient.java测试类 #### private static void serializable() throws IOException, ClassNotFoundException { User user = new User(); user.setName("测试static变量"); user.setAge(24); File file = new File("transient.txt"); if(!file.exists()){ file.createNewFile(); } ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file)); oos.writeObject(user); oos.close(); System.out.println("添加了static关键字序列化之前:user= " + user); user.setAge(18); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)); User newUser = (User) ois.readObject(); System.out.println("添加了static关键字序列化之后:newUser= " + newUser); } #### 运行结果 #### ![在这里插入图片描述][20200820162818677.png_pic_center] > 结果已经很明显了。现在解释一下,为什么会是这样,其实在前面已经提到过了。因为静态变量在全局区,本来流里面就没有写入静态变量,我打印静态变量当然会去全局区查找,而我们的序列化是写到磁盘上的,所以JVM查找这个静态变量的值,是从全局区查找的,而不是磁盘上。user.setAge(18);年龄改成18之后,被写到了全局区,其实就是方法区,只不过被所有的线程共享的一块空间。因此可以总结一句话: > 静态变量不管是不是transient关键字修饰,都不会被序列化 > 这个地方可能有点难理解,要利用反向思维,如果序列化成功,那么age=24,而不是18。 ## 三、transient关键字总结 ## > java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。像银行卡、密码等等这些数据。这个需要根据业务情况了。 [20200820151540846.png_pic_center]: /images/20221124/6e0acfc356b0422cad51d4b00d9f9431.png [20200820152642326.png_pic_center]: /images/20221124/536b32a26986453b87d3d0ef7fc45e69.png [20200820162818677.png_pic_center]: /images/20221124/4be25047a3b846d684dfb422e511a8f5.png
还没有评论,来说两句吧...