学习序列化时随手记录一下,便于以后的复习
程序运行时,实例化出的对象信息是加载到内存中的,当程序结束后,对象信息也就不存在了,怎么将对象信息保存到磁盘中呢?这就要用到对象序列化。
先看代码:
定义了一个用户类User:
1 class User{
2 String userName;
3 String passWord;
4
5 public User(String userName, String passWord) {
6 this.userName = userName;
7 this.passWord = passWord;
8 }
9
10 @Override
11 public String toString() {
12 return "用户名:" + this.userName + " 密码:" + this.passWord;
13 }
14 }
java为我们提供了对象序列化的操作流:ObjectOutputStream,我们把user对象的信息保存到F:\\obj.txt文件中去。
1 public static void main(String[] args) throws IOException{
2 //对象的序列化
3 User user = new User("admin", "123");
4 File file = new File("F:\\obj.txt");
5 FileOutputStream fileOutputStream = new FileOutputStream(file);
6 ObjectOutputStream objectOutput = new ObjectOutputStream(fileOutputStream);
7
8 objectOutput.writeObject(user);
9 objectOutput.close();
10
11 }
此时运行main程序,你会惊喜的发现,报错了!(Exception in thread "main" java.io.NotSerializableException),为什么会出现这个错误呢?原因是我们所写的User类没有实现Serializable(可序列化的)接口,将User实现此接口后,运行程序可将user对象实例化。查看F:\\obj.txt文件,里面确实有内容了(但是一堆乱码)。这说明实例化成功了。
如果我们想从磁盘中读取前面我们序列化的对象呢(这个过程叫做反序列化),so easy!java也为我们反序列化提供了ObjectInputStream,读取F:\\obj.txt:
1 public static void main(String[] args) throws IOException{
2 File file = new File("F:\\obj.txt");
3
4 FileInputStream fileInputStream = new FileInputStream(file);
5
6 ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
7 User user = (User) objectInputStream.readObject();
8 objectInputStream.close();
9 System.out.println(user);
10
11 }
控制台打印输出:用户名:admin 密码:123
反序列化成功!
继续深入一下,关于序列化问题,还有一些要说一说的东西。。
java中的集合类全部都实现了Serializable接口,观察它们的源代码,你会发现这些集合类中都会有这样一条语句(右边的数字会有所不同):
private static final long serialVersionUID = 876323262645176354L;
那么,这个serialVersionUID 有何作用呢?实现Serializable接口的类必须都要写吗?(显然不是,因为我们前面写的User类,里面就没有这玩意。。)
serialVersionUID字面意思是序列版本号,到底是个什么玩意呢,咱们接着看。
前面我们通过序列化,将user信息保存到了F:\\obj.txt中,现在对User类的结构做一些改变,比如增加一个age字段:
1 class User implements Serializable {
2 String userName;
3 String passWord;
4 int age;
5
6 public User(String userName, String passWord) {
7 this.userName = userName;
8 this.passWord = passWord;
9 }
10
11 @Override
12 public String toString() {
13 return "用户名:" + this.userName + " 密码:" + this.passWord;
14 }
15 }
此时我们直接读取obj.txt进行反序列化会发生什么呢?运行程序,编译器会报出如下错误信息:
java.io.InvalidClassException; local class incompatible: stream classdesc serialVersionUID = -4401750438685317474, local class serialVersionUID = 3946011404892452386
事实上,我们的User类中并没有定义出serialVersionUID 的值,是java自动为我们计算出来的,java根据一个类的字段信息、包信息等计算出serialVersionUID 字段的值,所以,如果我们对类结构做出了改变,那么serialVersionUID 的值一定会随之改变,所以在反序列化的时候,就会出现上面那条错误信息。
如果想在改变类结构的情况下,又不影响反序列化的执行,应该怎么做呢?很简单,我们可以在User类中显示定义serialVersionUID 的值,并加上final关键字。这样就可以轻松解决上述问题。