文章参考来源
一、Serializable是什么?在java中,Serializable是一个接口,一个对象序列化的接口,一个类只有实现了Serializable接口,它的对象才能被序列化。
二、什么是序列化?序列化是将对象状态转换为可保持或传输的格式的过程,将对象转换为流。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。
三、为什么要序列化对象?序列化对于面向对象的编程语言来说是非常重要的,因为无论什么编程语言,其底层涉及IO操作的部分还是由操作系统帮其完成的,而底层IO操作都是以字节流的方式进行的,所以写操作都涉及将编程语言数据类型转换为字节流,而读操作则又涉及将字节流转化为编程语言类型的特定数据类型。
四、使用举例// 创建一个Person对象
public class Person implements Serializable {
private static final long serialVersionUID = 2l;
public String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
// 将person实例对象写入文件中
public class Write {
public static void main(String[] args) throws IOException {
Person person = new Person("name", 23);
ObjectOutputStream objectOutputStream =
new ObjectOutputStream(new FileOutputStream("person.txt"));
System.out.println(person);
objectOutputStream.writeObject(person);
objectOutputStream.close();
}
}
// 从文件中读取person实例对象
public class Read {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream =
new ObjectInputStream(new FileInputStream("person.txt"));
Person person = (Person) objectInputStream.readObject();
System.out.println(person);
objectInputStream.close();
}
}
五、serialVersionUID简介
对于JVM来说,要进行持久化的类必须要有一个标记,只有持有这个标记JVM才允许类创建的对象可以通过其IO系统转换为字节数据,从而实现持久化,而这个标记就是Serializable接口。而在反序列化的过程中则需要使用serialVersionUID来确定由那个类来加载这个对象,所以我们在实现Serializable接口的时候,一般还会要去尽量显示地定义serialVersionUID。
这个serialVersionUID的详细的工作机制是:在序列化的时候系统将serialVersionUID写入到序列化的文件中去,当反序列化的时候系统会先去检测文件中的serialVersionUID是否跟当前的文件的serialVersionUID是否一致,如果一直反序列化不成功,就说明当前类跟序列化后的类发生了变化,比如是成员变量的数量或者是类型发生了变化,那么在反序列化时就会报出错误,详细错误信息如下
Exception in thread "main" java.io.InvalidClassException: JavaLearn.Serializable.Person; local class incompatible: stream classdesc serialVersionUID = 2, local class serialVersionUID = 1 at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2001) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1848) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2158) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1665) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:501) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:459) at JavaLearn.Serializable.read.main(read.java:12)
刚开始提到了,serialVersionUID要不要指定呢?如果不指定会出现什么样的后果?如果指定了以后后边的值又代表着什么意思呢?既然系统指定了这个字段,那么肯定是有它的作用的。
如果我们在序列化中没有显示地声明serialVersionUID,则序列化运行时将会根据该类的各个方面计算该类默认的serialVersionUID值。但是,Java官方强烈建议所有要序列化的类都显示地声明serialVersionUID字段,因为如果高度依赖于JVM默认生成serialVersionUID,可能会导致其与编译器的实现细节耦合,这样可能会导致在反序列化的过程中发生意外的InvalidClassException异常。因此,为了保证跨不同Java编译器实现的serialVersionUID值的一致,实现Serializable接口的必须显示地声明serialVersionUID字段。
此外serialVersionUID字段地声明要尽可能使用private关键字修饰,这是因为该字段的声明只适用于声明的类,该字段作为成员变量被子类继承是没有用处的!有个特殊的地方需要注意的是,数组类是不能显示地声明serialVersionUID的,因为它们始终具有默认计算的值,不过数组类反序列化过程中也是放弃了匹配serialVersionUID值的要求。



