什么是Serializable?
Serializable翻译成中文就是序列化,官方的翻译如下:
通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。
也就是Serializable是一个空接口,只是用来标记告诉JVM我这个对象是可序列化的,可以通过ObjectOutputStream/ObjectInputStream进行序列化与发序列化操作,否则报错java.io.NotSerializableException。
什么情况下需要Serializable
?
既然我们知道java是通过ObjectOutputStream/ObjectInputStream流的形式进行序列化与发序列化操作的,那么显而易见的以下几种情况是需要序列化:
- 当要对一个对象进行持久化操作的时候,比如保存为一个文件,或者在数据库中
- 当一个对象要进行跨进程或者跨线程传输的时候,比如网络传输,Android中的bundle等
- 通过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的一致性问题