最近在牛客网上刷面试题,然后看到关于java序列化的问题,由于自己之前没有遇到过相关的问题,所以稍微花了点时间了解了一下Serializable及关键字transient。本文参考了网络上的一些文章。
什么是序列化和反序列化,序列化的用途是什么?
序列化:把对象转化成字节序列的过程,称之为对象的序列化
反序列化:把字节序列恢复为对象的过程,称之为对象的反序列化
更形象的说法:
java序列化技术可以使你将一个对象的状态写入一个byte流里,并且可以从其他地方把该byte流里的数据读出来。
换种方式来说,就是把内存中的对象给变成一连串的字节描述的过程,最常见的是将其变成文件。
用途:
想把内存中的对象保存到一个文件中或者数据库中的时候
想把对象通过网络进行传播的时候
如何序列化?
(1)只要一个类实现了Serilizable接口,那么这个类就是可序列化的,这个类中的属性和方法都会自动序列化。若某个属性被声明为transient,则该属性不用被序列化。
(2)
若实现的是Externalizable接口,则没有任何东西可以自动序列化,需要在writeExternal方法中进行手工指定所要序列化的变量,这与是否被transient修饰无关。
其中,在实现
Serilizable接口的类中,可以使用transient关键字使得该对象不用序列化。
当一个类已经被序列化后,该类中的某个属性不需要被序列化,可以加上transient关键字。
例如,一个用户有一些敏感信息,为了安全起见,不希望在网络操作(序列化操作)中被传输,这些信息对应的变量加上transient关键字。
/**
* 使用transient关键字不序列化某个变量
*/
public class TransientTest {
public static void main(String[] args) {
User user = new User();
user.setUsername("tammy");
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 (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException 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 (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class User implements Serializable {
private static final long serialVersionUID = 8294180014912103005L;
private String username;
private transient String passwd;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
}
结果:
read before Serializable:
username: tammy
password:
123456
read after Serializable:
username: tammy
password: null
注意:transient关键字只能修饰变量,而不能修饰方法和类
一个被static修饰的变量,无论是否还被transient修饰,都不能被序列化。
实现Externalizable接口实现序列化,加不加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) 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();
}
}
运行结果:
是的,我将会被序列化,不管我是否被transient关键字修饰