Java I/O不迷茫,一文为你导航! (4)

Java序列化就是将一个对象转化成一串二进制表示的字节数组,通过保存或转移这些字节数据来达到持久化的目的。需要持久化,对象必须继承 java.io.Serializable 接口,或者将其转为字节数组,用于网络传输;

一个实际的序列化例子

第一步:创建一个用于序列化的对象

为了具体说明序列化在Java中是如何运作的,我们来写一个实际的例子,首先我们来写一个用于序列化的对象,然后实现上述的接口:

/** * 用于演示Java中序列化的工作流程... * * @author: @我没有三颗心脏 * @create: 2018-08-15-下午 14:37 */ public class People implements Serializable{ public String name; public transient int age; public void sayHello() { System.out.println("Hello,My Name is " + name); } }

注意:一个类的对象想要序列化成功,必须满足两个条件

①实现上述的接口;

②保证该类的所有属性必须都是可序列化的,如果不希望某个属性序列化(例如一些敏感信息),可以加上transient关键字;

第二步:序列化对象

如下的代码完成了实例化一个 People 对象并其序列化到D盘的根目录下的一个操作,这里呢按照 Java 的标准约定将文件的后缀写成 .ser 的样子,你也可以写成其他的...

People people = new People(); people.name = "我没有三颗心脏"; people.age = 21; try { FileOutputStream fileOutputStream = new FileOutputStream("D:/people.ser"); ObjectOutputStream out = new ObjectOutputStream(fileOutputStream); out.writeObject(people); out.close(); fileOutputStream.close(); System.out.println("Serialized data is saved in D:/"); } catch (IOException e) { e.printStackTrace(); }

第三步:反序列化对象

下面的程序完成了对刚才我们序列化的文件还原成一个People对象的过程,并获取了其中的参数,但是注意,由于我们希望 age 属性是短暂的加入了transient关键字, 所以我们无法获取到序列化时 People 的 age 属性:

People people = null; try { FileInputStream fileIn = new FileInputStream("D:/people.ser"); ObjectInputStream in = new ObjectInputStream(fileIn); people = (People) in.readObject(); in.close(); fileIn.close(); } catch (IOException i) { i.printStackTrace(); return; } catch (ClassNotFoundException c) { System.out.println("People class not found"); c.printStackTrace(); return; } System.out.println("Deserialized People..."); System.out.println("Name: " + people.name); System.out.println("Age: " + people.age);

输出结果如下:

Deserialized People... Name: 我没有三颗心脏 Age: 0 serialVersionUID的作用

上述的例子中我们完成了对一个 People 对象序列化和反序列化的过程,我们现在来做一点简单的修改,例如把age字段的transient关键字去掉:

public class People implements Serializable { public String name; public int age; public void sayHello() { System.out.println("Hello,My Name is " + name); } }

然后我们再运行我们刚才反序列化的代码,会发现,这个时候程序竟然报错了,说是serialVersionUID不一致:

Java I/O不迷茫,一文为你导航!

事实上,如果你经常看别人的代码的话,或许会有留意到诸如这样的代码:

private static final long serialVersionUID = 876323262645176354L;

就这一长串的东西也不知道是在干嘛的,但这其实是为了保证序列化版本的兼容性,即在版本升级后序列化仍保持对象的唯一性;我们通过上述的修改也感受到了其中的一二,但是问题是:我们并没有在需要序列化的对象中写任何关于这个UID的代码呀?

这是个有趣的问题,通常情况下,如果我们实现了序列化接口,但是没有自己显式的声明这个UID的话,那么JVM就会根据该类的类名、属性名、方法名等自己计算出一个独一无二的变量值,然后将这个变量值一同序列化到文件之中,而在反序列化的时候同样,会根据该类计算出一个独一无二的变量然后进行比较,不一致就会报错,但是我怀着强烈的好奇心去反编译了一下.class文件,并没有发现编译器写了UDI这一类的东西,我看《深入分析 Java Web 技术内幕》中说,实际上是写到了二进制文件里面了;

不显式声明的缺点:一旦写好了某一个类,那么想要修改就不行了,所以我们最好自己显式的去声明;

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpwsyj.html