Java中的Serializable与serialVersionUID(一)

什么是Serializable?

Serializable翻译成中文就是序列化,官方的翻译如下:

通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。

也就是Serializable是一个空接口,只是用来标记告诉JVM我这个对象是可序列化的,可以通过ObjectOutputStream/ObjectInputStream进行序列化与发序列化操作,否则报错java.io.NotSerializableException。

什么情况下需要Serializable

既然我们知道java是通过ObjectOutputStream/ObjectInputStream流的形式进行序列化与发序列化操作的,那么显而易见的以下几种情况是需要序列化:

  1. 当要对一个对象进行持久化操作的时候,比如保存为一个文件,或者在数据库中
  2. 当一个对象要进行跨进程或者跨线程传输的时候,比如网络传输,Android中的bundle等
  3. 通过RMI(Remote Method Invoke远程方法调用)传输对象的时候;

怎么进行序列化与反序列化?

看代码:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class TestSerializable {

  public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
//     序列化
    ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("serializable.txt"));
    User user = new User("jack", 18);
    objectOutputStream.writeObject(user);
    objectOutputStream.close();
    //反序列化
    ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("serializable.txt"));
    User user1 = (User) inputStream.readObject();
    inputStream.close();
    System.out.println(user1);
    System.out.println("user == user1:"+(user == user1));

  }

  static class User implements Serializable {
//    /**
//     * 
//     */
//    private static final long serialVersionUID = 1L;
//    /**
//     * 
//     */
    String name;
    int age;

    public User(String name, int age) {
      super();
      this.name = name;
      this.age = age;
    }

    @Override
    public String toString() {
      // TODO Auto-generated method stub
      return "名字是" + name+",年龄是"+age;
    }

  }

}
复制代码

运行,刷新项目,可以发现项目的根目录下生成了文件serializable.txt,结果如下:

名字是jack,年龄是18
user == user1:false
复制代码

可以发现User对象的状态可以正确的还原,注意的的是user == user1为false,这点其实也好理解,user1可以理解为照着user的样子重新构造的一个对象,二者指向的内存是不一样的,只有通过user1 = user这样的操作,二者才能相等。

       接下来吧// private static final long serialVersionUID = 1L;这行的注释拿掉,继续运行发现没有什么区别啊。别急继续,// private static final long serialVersionUID = 1L;注释掉这行,运行一下,给User加上一个属性id,吧上面的序列化写的过程注释掉,也就说Serializable.txt这个时候保存的User是没有id的,然后进行发序列化,尴尬了:

 in thread "main" java.io.InvalidClassException: TestSerializable$User; local class incompatible: stream classdesc serialVersionUID = -7167179755746730123, local class serialVersionUID = 6006218782893099499

直接崩溃了。说本地的serialVersionUID与类描述的serialVersionUID不兼容。那好吧我们在一开始序列化的时候就指定serialVersionUID,然后修改User看看是否有效果。

代码如下:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class TestSerializable {

  public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
//     序列化
    ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("serializable.txt"));
    User user = new User("jack", 18);
    objectOutputStream.writeObject(user);
    objectOutputStream.close();
    //反序列化
    ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("serializable.txt"));
    User user1 = (User) inputStream.readObject();
    inputStream.close();
    System.out.println(user1);
    System.out.println("user == user1:"+(user == user1));

  }

  static class User implements Serializable {
//    /**
//     * 
//     */
    private static final long serialVersionUID = 1L;
//    /**
//     * 
//     */
    String name;
    int age;

    public User(String name, int age) {
      super();
      this.name = name;
      this.age = age;
    }

    @Override
    public String toString() {
      // TODO Auto-generated method stub
      return "名字是" + name+",年龄是"+age;
    }

  }

}
复制代码

运行没问题,接下来吧序列化过程注释掉,给User同样加上id,运行发现正常。

总结一下:

Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。如果不指定的JVM会根据对象的属性比如成员变量的类型等信息自动生成,当对象发生改变时,serialVersionUID往往也是不一样的,就会报错。所以java强烈建议制定serialVersionUID,避免有些时候代码修改之后的崩溃。当然如果你把对象该的面目全非,也是不行的,因为类的结构都不一样了,不能指鹿为马,比如上面的你用User的serializable.txt,然后调用反序列化生成Student,那肯定不行。

      这是java中Serializable的第一篇,下一篇我们将从源码的角度,深入分析,为啥么只有序列化对象才能进行传输持久化等操作,以及serialVersionUID的一致性问题


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值