关键字transient的作用
我们知道一个对象只要实现了Serializable接口,那么它就可以序列化。我们不必关心序列化的过程,只要这个类实现了Serializable接口,那么这个类的所有属性和方法都会自动序列化。
但是,在真实项目开发中,我们可能会遇到这样的问题:用户的敏感信息(如银行卡、密码等),我们可能不希望它在网络中传输。那么我们就可以使用transient关键在来修饰这些属性。那么,这些被transient修饰的属性就不会被序列化。
我们来验证一下:
public class User implements Serializable {
private String username;
private transient String passwd;
//省略getter/setter方法
}
public class TransientTest {
public static void main(String[] args) {
User user = new User();
user.setUsername("zhangsan");
user.setPasswd("123456");
System.out.println("read before serializable:");
System.out.println("username:" + user.getUsername());
System.out.println("password:" + user.getPasswd());
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:/user.txt"));
oos.writeObject(user);
oos.flush();
oos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:/user.txt"));
user = (User)ois.readObject();
ois.close();
System.out.println("\nread after serializable:");
System.out.println("username:" + user.getUsername());
System.out.println("password:" + user.getPasswd());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出结果如下:
read before Serializable:
username: zhangsan
password: 123456
read after Serializable:
username: zhangsan
password: null
由此说明,被transient修饰的passwd属性没有被序列化。
transient使用小结
- 被transient修饰的属性无法被序列化
- transient关键字只能修饰属性,不能修饰类和方法。
- 如果变量类型是用户自定义类型,那么就要实现Serializable接口,否则不能序列化。
- static类型的变量无论是否被transient关键字修饰,都不会被序列化。
第4点,可能会有很多人怀疑。在为passwd属性增加了static关键字之后,发现还是能够获取到值的。如下:
import java.io.Serializable;
public class User implements Serializable {
private String username;
private static String passwd;
//省略getter和getter方法
}
运行结果如下:
read before serializable:
username:zhangsan
password:123456
read after serializable:
username:zhangsan
password:123456
其实这个passwd属性的值不是从反序列化中得到的,而是JVM中对应静态变量的值。不信的话,我们来验证一下:
public class TransientTest {
public static void main(String[] args) {
User user = new User();
user.setUsername("zhangsan");
user.setPasswd("123456");
System.out.println("read before serializable:");
System.out.println("username:" + user.getUsername());
System.out.println("password:" + user.getPasswd());
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:/user.txt"));
oos.writeObject(user);
oos.flush();
oos.close();
user.setPasswd("changePassword"); //在序列化后修改密码
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:/user.txt"));
user = (User)ois.readObject();
ois.close();
System.out.println("\nread after serializable:");
System.out.println("username:" + user.getUsername());
System.out.println("password:" + user.getPasswd());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
运行结果:
read before serializable:
username:zhangsan
password:123456
read after serializable:
username:zhangsan
password:changePassword
被transient修饰的变量真的不能被序列化吗?
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) {
ExternalizableTest et = new ExternalizableTest();
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:/content.txt"));
oos.writeObject(et);
oos.flush();
oos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:/content.txt"));
et = (ExternalizableTest) ois.readObject();
ois.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(et.content);
}
}
输出结果如下:
是的,我将会被序列化,不管我是否被transient修饰
这是为什么呢?不是说被transient修饰的变量不能被序列化
我们知道在java中,对象的序列化可以通过实现两种接口进行实现。若实现的是Serializable接口,那么所有的序列化会自动进行。若实现的是Externalizable接口,则没有任何东西自动序列化,需要在writeExternal方法中手动指定所要序列化的变量。
这就是为什么上面这个例子,content可以被序列化的原因。