java 中的 Serializable 接口是用于实现对象 序列化和反序列化 的功能。那么什么是序列化和反序列化呢?
- 序列化: 就是将对象的当前状态写入字节流,用于保存或传输的过程
- 反序列化:则是从字节流中恢复对象,是序列化的逆过程
通过序列化,能够将当前 java 对象保存至文件,或通过网络传输,并在需要时通过反序列化进行恢复。
序列化和反序列化
只有实现了 Serializable 接口的类才能通过序列化功能进行保存和恢复。下面进行示例说明:
- 下面定义了一个名为 UserInfo 的类,并实现 Serializable 接口
public class UserInfo implements Serializable {
private static final long serialVersionUID = -7701101573245777694L;
private String username;
private String password;
public UserInfo(String username, String password) {
this.username = username;
this.password = password;
}
@Override
public String toString() {
return "UserInfo [ username=" + username + ", password=" + password + "]";
}
}
- 通过 ObjectOutputStream 进行序列化输出
public class SerializeDemo {
public static void main(String[] args) {
UserInfo userinfo = new UserInfo("eric", "eric_123456");
try (FileOutputStream fos = new FileOutputStream(new File("demo.txt"));
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(userinfo);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
我们看下demo.txt文件夹内容
��sr*com.eric.learning.java._serialize.UserInfo� 0脎{肉��Lpasswordt�Ljava/lang/String;Lusernameq~�xpt�eric_123456t�eric
序列化后的文件无法直接查看,但是文件中可以看到上文设置的成员变量值 eric 和 eric_123456
- 我们再通过 ObjectInputStream 进行反序列化恢复:
public class SerializeDemo {
public static void main(String[] args) {
UserInfo userinfo = null;
try (FileInputStream fis = new FileInputStream(new File("demo.txt"));
ObjectInputStream ois = new ObjectInputStream(fis)) {
userinfo = (UserInfo) ois.readObject();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(userinfo);
}
}
输出结果为:
UserInfo [username=eric, password=eric_123456]
serialVersionUID
在示例中,我们在实现 Serializable 接口时,生成了一个变量 serialVersionUID ,这个变量是干嘛用的呢?
实际上这个变量是可序列化类的一个版本标识。 在序列化对象时,对象的 serialVersionUID 会被一同写入到字节流,而反序列化时 JVM 会将字节流中读取的 serialVersionUID 同类的 serialVersionUID 比较,如果不相同则会反序列化失败。
如果实现了 Serializable 接口,但不指定 serialVersionUID 的值,那么编译器在编译时会自动生成一个 serialVersionUID.
示例:
- 假设我们增加了 Userinfo 中的字段:
public class UserInfo implements Serializable {
private static final long serialVersionUID = -7701101573245777694L;
private Long id;
private String username;
private String password;
}
更新后,我们希望旧 UserInfo 对象的序列化结果,无法再反序列化到新 UserInfo 类对象,这时我们可以重新生成 serialVersionUID 让旧对象的序列化结果失效。
public class UserInfo implements Serializable {
// before
// private static final long serialVersionUID = -7701101573245777694L;
// now
private static final long serialVersionUID = -7701101573245777694L
private Long id;
private String username;
private String password;
}
这时如果反序列化旧 UserInfo 对象,将抛以下错误:
java.io.InvalidClassException: com.eric.learning.java._serialize.UserInfo; local class incompatible: stream classdesc serialVersionUID = -7701101573245777694, local class serialVersionUID = -7701101573245777695
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1829)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1713)
transient
在 UserInfo 类中,有个用于存储用户密码的 password 字段,密码属于私密信息,我们不希望其被随意泄露。这时我们可以使用 transient 关键字。
用 transient 修饰的字段,在序列化时将被忽略:
public class UserInfo implements Serializable {
private static final long serialVersionUID = -7701101573245777694L;
private String username;
private transient String password; // transient 修饰
public UserInfo(String username, String password) {
this.username = username;
this.password = password;
}
}
再次序列化输出到文件:
public class SerializeDemo {
public static void main(String[] args) {
UserInfo userinfo = new UserInfo("eric", "eric_123456");
try (FileOutputStream fos = new FileOutputStream(new File("demo.txt"));
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(userinfo);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
文件内容中已经没有 password 内容:
��sr*com.eric.learning.java._serialize.UserInfo� 0脎{肉��Lusernamet�Ljava/lang/String;xpt�eric