一、序列化是什么
在jvm中引用数据类型存在于栈中,而new创建出的对象存在于堆中。如果电脑断电那么存在于内存中的对象就会丢失。那么有没有方法将对象保存到磁盘(对象持久化存储)或通过网络传输到远处的其他地方呢?
答案是可以,但是我们必须要求所有支持持久化存储的类实现Serializable接口。原因是,jvm不仅需要考虑将对象存储到硬盘等其他介质,还需要考虑将其读取(反序列化)出来。
如果读取在硬盘中的对象不能被创建它的类识别,岂不会产生问题?序列化就是起到告诉JVM,这个对象是由哪个类创建的。
对于jvm来说,要进行持久化的类必须要有个标记,相当于一个通行证,只有持有这个通行证,jvm才让类创建的对象进行持久化。这个通行证就是Serializable接口,这个接口将类与一个称为serialVersionUID的变量关联起来,这个serialVersionUID就是在反序列化中用来确定由哪个类来加载这个对象。
说明白点序列化是将对象状态转换为可保持或传输的格式的过程,就是你可以用对象输出流输出到文件.
二、实现Serializable接口的例子
这个例子的作用是将一个学生信息存储到磁盘中,然后再将其读取出来:
public class student implements Serializable {
public static String countryName="china";
private int id;
private String name;
private String sex;
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
String s="adaf";
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class ObjectStreamDemo {
public static void main(String[] args) {
//writeObj();
readObj();
}
public static void writeObj()
{
student s=new student();
s.setId(8);
s.setName("张三");
s.countryName="USA";
try {
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("c:\\obj.txt"));
oos.writeObject(s);
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void readObj()
{
try {
ObjectInputStream ooi=new ObjectInputStream(new FileInputStream("c:\\obj.txt"));
try {
Object obj=ooi.readObject();
student s=(student)obj;
//person s=(person)obj;
System.out.println("id:"+s.getId()+",name:"+s.getName()+s.countryName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
ooi.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
可以看到程序正常运行,但是我们给student类添加一个成员变量private string school,那么则在读取时候抛出异常:
java.io.InvalidClassException:com.lei.lock.student; local class incompatible: stream classdescserialVersionUID = 5039200702392716702, local class serialVersionUID =-858199357477413536
大意为:本地类不匹配:保存在本地的文件描述类序列号为5039200702392716702 ,而当前类的序列号为-858199357477413536。因此我们可以得出结论,如果不指定serialVersionUID,那么其值是和类中的成员变量相关联的。
同时需要注意,持久化的数据均为存在于堆中的数据,static类型的数据存在于方法区中,不能被持久化。如果想不持久化某个成员变量,则需要在成员变量加上关键字transient
三、自定义serialVersionUID
既然是由serialVersionUID来决定由哪个类来加载存在于文件中的对象,那么我们能不能指定serialVersionUID的数值,使得其不再与类的成员变量相关联呢?答案是肯定的。
例如:
需要注意的地方是,serialVersionUID不是决定由哪个类加载硬盘文件中的唯一因素,类的包名、类的名称都有关联。如果不一致,也会出现类型转化错误。原因是类的包名,类名已经被写入了文件当中。
public class student implements Serializable {
public static String countryName="china";
private int id;
private String name;
private String sex;
private String school;
private static final long serialVersionUID = -5182532647273106745L;
//如果想对非静态的数据也不想序列化,则需要加入关键字
transient int height;//身高不存储到文件系统或者数据库中
public static String getCountryName() {
return countryName;
}
public static void setCountryName(String countryName) {
student.countryName = countryName;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
String s="adaf";
return name;
}
public void setName(String name) {
this.name = name;
}
}