标签:username 关键字 transient user 修饰 new 序列化
java中的关键字transient,这篇文章你再也不发愁了
1、概述
- 给不需要序列化的属性前添加
transient
关键字,序列化对象时,这个被修饰的属性就不会被序列化 - 比如用户对象中一些敏感信息(密码,银行卡号等),不希望在网络操作中传输或者存在磁盘中,即这些敏感信息只存在于内存中,不希望被序列化到磁盘存储,这些信息的生命周期只存在于调用者的内存而不会写到磁盘持久化。
2、序列化和反序列化
- 序列化:把对象转化为字节序列的过程。
- 反序列化:把字节序列恢复为对象的过程。
- 对象的序列化用途:
- 把对象的字节序列永久保存到磁盘中;
- 在网络上传送对象的字节序列。
在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。
当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象
3、序列化的两种方式
(1)实现Serializable接口
只要实现此接口,会自动序列化处理
/**
* @description 使用transient关键字不序列化某个变量
* 注意读取的时候,读取数据的顺序一定要和存放数据的顺序保持一致
*
* @author Alexia
* @date 2013-10-15
*/
class User implements Serializable {
private static final long serialVersionUID = 8294180014912103005L;
private String username;
private transient String passwd;
//setter、getter方法生成
}
public class TransientTest {
public static void main(String[] args) {
User user = new User();
user.setUsername("Alexia");
user.setPasswd("123456");
System.out.println("read before Serializable: ");
System.out.println("username: " + user.getUsername());
System.err.println("password: " + user.getPasswd());
try {
ObjectOutputStream os = new ObjectOutputStream(
new FileOutputStream("C:/user.txt"));
os.writeObject(user); // 将User对象写进文件
os.flush();
os.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
ObjectInputStream is = new ObjectInputStream(new FileInputStream(
"C:/user.txt"));
user = (User) is.readObject(); // 从流中读取User的数据
is.close();
System.out.println("\nread after Serializable: ");
System.out.println("username: " + user.getUsername());
System.err.println("password: " + user.getPasswd());
} catch (Exception e) {
e.printStackTrace();
}
}
}
solution:
read before Serializable:
username: Alexia
password: 123456
read after Serializable:
username: Alexia
password: null
因为private transient String passwd;
passwd字段被transient
修饰,所以password属性值不会被存储到磁盘中,反序列时就无法从文件中读取到该字段的属性值。
-
transient使用:
- 被transient修饰的变量无法被序列化
- transient只能修饰变量,不能修饰方法和类。本地变量(局部变量)不能被transient修饰。
- 静态变量(全局量)不管是否被transient修饰,都不能被序列化
针对第三点说明:因为发现在User类中的username字段前加上static关键字后,程序运行结果依然不变,即static类型的username也读出来为“Alexia”了,这不与第三点说的矛盾吗?实际上是这样的:第三点确实没错(一个静态变量不管是否被transient修饰,均不能被序列化),反序列化后类中static型变量username的值为当前JVM中对应static变量的值,这个值是JVM中的,不是反序列化得出的。因为静态变量在全局区,程序读取的时候会先到全局区读取数据。
/**
* @description 使用transient关键字不序列化某个变量
* 注意读取的时候,读取数据的顺序一定要和存放数据的顺序保持一致
*
* @author Alexia
* @date 2013-10-15
*/
class User implements Serializable {
private static final long serialVersionUID = 8294180014912103005L;
public static String username;
private transient String passwd;
//getter,setter方法生成
}
public class TransientTest {
public static void main(String[] args) {
User user = new User();
user.setUsername("Alexia");
user.setPasswd("123456");
System.out.println("read before Serializable: ");
System.out.println("username: " + user.getUsername());
System.err.println("password: " + user.getPasswd());
try {
ObjectOutputStream os = new ObjectOutputStream(
new FileOutputStream("C:/user.txt"));
os.writeObject(user); // 将User对象写进文件
os.flush();
os.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
// 在反序列化之前改变username的值
User.username = "jmwang";
ObjectInputStream is = new ObjectInputStream(new FileInputStream(
"C:/user.txt"));
user = (User) is.readObject(); // 从流中读取User的数据
is.close();
System.out.println("\nread after Serializable: ");
System.out.println("username: " + user.getUsername());
System.err.println("password: " + user.getPasswd());
} catch (Exception e) {
e.printStackTrace();
}
}
}
solution:
read before Serializable:
username: Alexia
password: 123456
read after Serializable:
username: jmwang
password: null
这说明反序列化后类中static型变量username的值为当前JVM中对应static变量的值,为修改后jmwang,而不是序列化时的值Alexia。
(2)实现Externalizable接口
实现此接口,需要重写writeExternal和readExternal方法,决定了哪些属性需要序列化。即使是transient修饰的属性,也可以指定该属性序列化。
/**
* @descripiton Externalizable接口的使用
*
* @author Alexia
* @date 2013-10-15
*
*/
public class ExternalizableTest implements Externalizable {
private transient String content = "是的,我将会被序列化,不管我是否被transient关键字修饰";
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(content);
}
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
content = (String) in.readObject();
}
public static void main(String[] args) throws Exception {
ExternalizableTest et = new ExternalizableTest();
ObjectOutput out = new ObjectOutputStream(new FileOutputStream(
new File("test")));
out.writeObject(et);
ObjectInput in = new ObjectInputStream(new FileInputStream(new File(
"test")));
et = (ExternalizableTest) in.readObject();
System.out.println(et.content);
out.close();
in.close();
}
}
solution:
是的,我将会被序列化,不管我是否被transient关键字修饰
原因是在writeExternal和readExternal中指定了对content属性的序列化,此时transient修饰就失效了。
4、总结
- transient关键字可以使得被修饰属性,不被序列化,保护敏感信息;
- transient只能修饰变量,不能修饰方法和类,也不能修饰本地变量;
- 静态变量不管有无transient修饰,都不能被序列化,都是从JVM全局区中读取,而不是从磁盘读取;
- 序列化实现是Serializable接口自动序列化,如变量被transient修饰,变量就不会被序列化;
- 序列化实现的是Externalizable接口,需要重写writeExternal和readExternal方法,如果在方法中指定了序列化的变量,那么这个变量必被序列化,不管此变量是否被transient修饰。
标签:username,关键字,transient,user,修饰,new,序列化 来源: https://www.cnblogs.com/jayzou/p/14395522.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。