在序列化的使用中我们常常遇到这样的问题,对于某些类对象进行序列化传输时,它的某些属性不希望被序列化传输,基于保密等方面。这个时候我们只需要在不希望被序列化的属性前面加上transient关键字即可。
举个例子:
构建一个User类
public class User implements Serializable {
public String name;
public transient String passwd; //passwd不希望被序列化,在内存中临时的存在
public User(String name,String passwd)
{
this.name=name;
this.passwd=passwd;
}
}
构建一个测试类
public class Test {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
User a=new User("sean","123");
System.out.println("a's name:"+a.name+" "+"a's passwd:"+a.passwd);
ObjectOutputStream os=new ObjectOutputStream(new FileOutputStream("C:/testtransient.txt"));
os.writeObject(a);
os.flush();
os.close();
ObjectInputStream is=new ObjectInputStream(new FileInputStream("C:/testtransient.txt"));
User b=(User) is.readObject();
is.close();
System.out.println("b's name:"+b.name+" "+"b's passwd:"+b.passwd);
}
}
运行结果:
a's name:sean a's passwd:123
b's name:sean b's passwd:null
从运行结果可以看出,属性passwd不能被序列化,通过反序列化生成的对象b没有密码
小结:
1、一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。
2、transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。
3、被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。
关于第三点,再举个例子说明:
public class User implements Serializable {
public String name;
public static String passwd; //把passwd改为静态变量
public User(String name,String passwd)
{
this.name=name;
this.passwd=passwd;
}
}
测试程序不变:
public class Test {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
User a=new User("sean","123");
System.out.println("a's name:"+a.name+" "+"a's passwd:"+a.passwd);
ObjectOutputStream os=new ObjectOutputStream(new FileOutputStream("C:/testtransient.txt"));
os.writeObject(a);
os.flush();
os.close();
ObjectInputStream is=new ObjectInputStream(new FileInputStream("C:/testtransient.txt"));
User b=(User) is.readObject();
is.close();
System.out.println("b's name:"+b.name+" "+"b's passwd:"+b.passwd);
}
}
运行结果:
a's name:sean a's passwd:123
b's name:sean b's passwd:123
看到这个结果是不是很奇怪,明明说静态变量不能被序列化,但是对象b依然有passwd,其实原因在于,静态变量确实不能被序列化,passwd变量没有被序列化导出到文件中,但是在反序列化对象b的时候,passwd变量自动从JVM中获取了当前内存中a对象的passwd值。如果不相信,咱们修改一下测试类做个试验:
public class Test {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
User a=new User("sean","123");
System.out.println("a's name:"+a.name+" "+"a's passwd:"+a.passwd);
ObjectOutputStream os=new ObjectOutputStream(new FileOutputStream("C:/testtransient.txt"));
os.writeObject(a);
os.flush();
os.close();
a.passwd="456"; //在反序列化前修改对象a的密码为456
ObjectInputStream is=new ObjectInputStream(new FileInputStream("C:/testtransient.txt"));
User b=(User) is.readObject();
is.close();
System.out.println("b's name:"+b.name+" "+"b's passwd:"+b.passwd);
}
}
运行结果:
a's name:sean a's passwd:123
b's name:sean b's passwd:456
反序列化前将a对象的密码改为456,反序列化后对象b的密码也为456,充分证明了上述观点。
另一个证明办法:
将原来的测试类拆分为两个测试类,如下:
public class Test {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
User a=new User("sean","123");
System.out.println("a's name:"+a.name+" "+"a's passwd:"+a.passwd);
ObjectOutputStream os=new ObjectOutputStream(new FileOutputStream("C:/testtransient.txt"));
os.writeObject(a);
os.flush();
os.close();
}
}
public class Test2 {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
ObjectInputStream is=new ObjectInputStream(new FileInputStream("C:/testtransient.txt"));
User b=(User) is.readObject();
is.close();
System.out.println("b's name:"+b.name+" "+"b's passwd:"+b.passwd);
}
}
分别运行测试类Test和Test2,Test2运行结果如下:
b's name:sean b's passwd:null
因为反序列化过程在另一个程序中,由于内存空间不同,对象b的passwd就不能去获取对象a的passwd值了,输出空密码就暴露了静态变量不能被序列化。