我们知道,在jvm中引用数据类型存在于栈中,而new创建出的对象存在于堆中。如果电脑断电那么存在于内存中的对象就会丢失。那么有没有方法将对象保存到磁盘(对象持久化存储)或通过网络传输到远处的其他地方呢?
答案是可以,但是我们必须要求所有支持持久化存储的类实现Serializable接口。原因是,jvm不仅需要考虑将对象存储到硬盘等其他介质,还需要考虑将其读取(反序列化)出来。
如果读取在硬盘中的对象不能被创建它的类识别,岂不会产生问题?序列化就是起到告诉JVM,这个对象是由哪个类创建的。
Java源码中的解释:
The serialization runtime associates with each serializable class a version number, called a serialVersionUID, which is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization.
对于jvm来说,要进行持久化的类必须要有个标记,相当于一个通行证,只有持有这个通行证,jvm才让类创建的对象进行持久化。这个通行证就是Serializable接口,这个接口将类与一个称为serialVersionUID的变量关联起来,这个serialVersionUID就是在反序列化中用来确定由哪个类来加载这个对象。
public class SerializableDemo implements Serializable {
// static 静态数据不会被序列化
private static String countryName = "China";
private int id;
private String name;
//如果想对非静态的数据也不想序列化,则需要加入关键字 transient, 身高不存储到文件系统或者数据库中
private transient int height;
// 需要特别注意的是,如果使用默认的serialVersionUID ,那么这个serialVersionUID是和类的字段相关联的,
// 如果修改了类的信息, 那么可能报java.io.InvalidClassException, 因为之前写入的时候的serialVersionUID和
// 现在去读取信息的serialVersionUID已经因为类的信息的修改而不一致了。所以最好使用自定义的serialVersionUID
//private String school;
public static String getCountryName() {
return countryName;
}
public static void setCountryName(String countryName) {
SerializableDemo.countryName = countryName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
// public String getSchool() {
// return school;
// }
//
// public void setSchool(String school) {
// this.school = school;
// }
}
public class SerializableDemoTest {
public static void main(String[] args) {
//writeObj();
readObj();
}
public static void writeObj() {
SerializableDemo sd = new SerializableDemo();
sd.setId(1);
sd.setName("kevin");
sd.setHeight(175);
try {
FileOutputStream fos = new FileOutputStream("logs/log1.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(sd);
oos.close();
}
catch (Exception e) {
e.printStackTrace();
}
}
public static void readObj() {
try {
FileInputStream fis = new FileInputStream("logs/log1.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
Object object = ois.readObject();
SerializableDemo sd = (SerializableDemo)object;
System.out.println(sd.getId() + "--" + sd.getName() + "--" + sd.getHeight());
ois.close();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
需要注意的地方是,serialVersionUID不是决定由哪个类加载硬盘文件中的唯一因素,类的包名、类的名称都有关联。如果不一致,也会出现类型转化错误。原因是类的包名,类名已经被写入了文件当中。