Java序列化
序列化就是一种用来存储对象的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对象流进行读写操作时所引发的问题。序列化的目的还是存储和传输数据。
序列化的实现
1. 将需要被序列化的类实现Serializable接口,
2. 使用一个输出流(如FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,
3. 使用ObjectOutputStream对象的writeObject(Object obj)方法可以将对象输出到文件后者其他地方,要恢复的话则用输入流。
哪些内容可以序列化
n 声明为static和transient类型的成员数据不能被序列化。static代表类的状态,transient代表对象的临时数据;
n 只有属性值才需要序列化,方法是公共代码,不需要序列化的。
n 没有实现序列化的成员属性是不能直接序列化的,它的状态会被设置为null;
n 如果父对象实现了序列化接口,则子对象也可以被序列化。
n 如果子对象实现了序列化接口,父类没实现序列化接口时,序列化子类的过程没问题,但是反序列化过程是通过反射载入创建实例的,会调用父类的无参数构造参数,所以父类必须有一个无参数构造函数。
自定义序列化
//下面的方法会在序列化的过程中被调用
1. private void writeObject(ObjectOutputStream out){
2. try {
3. out.defaultWriteObject(); //这个方法会把这当前中非静态变量和非transient变量写到流中
4. //下面的语句可以把任何序列化的对象的一些值放进去,尽管这个对象没有实现序列化接口,实际上是把这个对象的成员序列化进去了。
5. out.writeInt(book.getIsbn());//ObjectOutputStream中提供了写基本类型数据的方法
6. //out.close();//注意,这句千万不能有,否刚将直接导致写入失败
7. } catch (IOException e) {
8. e.printStackTrace();
9. }
10. }
11.
12. //这个方法会在反序列化的过程中被自动调用
13. private void readObject(ObjectInputStream in){
14. try {
15. in.defaultReadObject(); //和defaultWriteObject()方法相对应,默认的反序列化方法,会从流中读取
16. int isbn = in.readInt(); //用这个方法来读取一个int型值。
17. book = new Book(isbn); //这里我们就通过读取的保存的状态构造了一个和原来一样的Book对象
18. //in.close();同样的这句也不能有
19. } catch (IOException e) {
20. e.printStackTrace();
21. } catch (ClassNotFoundException e) {
22. e.printStackTrace();
23. }
24. }
从自定义序列化的过程中也能看出,序列化在具体实施过程中是怎样的一个过程。
在向ObjectOutputStream中写入序列化对象,执行writeObject方法时,会首先执行序列化对象中的writeObject方法,如果没有这个方法,则执行流默认的写入方法,默认的写入方法只会写入非静态和非临时的对象。
如果序列化对象有writeObject方法,则在方法中先调用流对象的默认写入方法,然后可以自定义的再写入其他实现序列化接口的成员。从这个过程也能看出,只有成员变量才能够被写入,而方法是不能写入的。
序列化的难点总结
1,静态变量、临时变量、方法,都不会被序列化出去,而是直接赋值null;
2,没有实现序列化接口的对象成员,可以添加writeObject方法,在方法中写入该对象成员的成员。
3,父类没有实现序列化,子类实现序列化,在序列化没问题,反序列化时需要父类必须有无参构造函数。
4,序列化ID,是序列化接口的一个成员,序列化唯一性的标识符。
序列化ID的作用:
序列化ID决定着是否能够成功反序列化!简单来说,java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地实体类中的serialVersionUID进行比较,如果相同则认为是一致的,便可以进行反序列化,否则就会报序列化版本不一致的异常。等会我们可以通过代码验证一下。
序列化ID如何产生:
当我们一个实体类中没有显示的定义一个名为“serialVersionUID”、类型为long的变量时,Java序列化机制会根据编译时的class自动生成一个serialVersionUID作为序列化版本比较,这种情况下,只有同一次编译生成的class才会生成相同的serialVersionUID。譬如,当我们编写一个类时,随着时间的推移,我们因为需求改动,需要在本地类中添加其他的字段,这个时候再反序列化时便会出现serialVersionUID不一致,导致反序列化失败。那么如何解决呢?便是在本地类中添加一个“serialVersionUID”变量,值保持不变,便可以进行序列化和反序列化。