浅析关于java的序列化和反序列化
作者:yasooooo
对于序列化和反序列化,大家或多或少都会听过一点。
所谓序列化,就是把要传输的对象以及相关信息转换成字节数组进行存储的过程。
而反序列化就是将字节数组再转回对象的过程。
对于序列化和反序列化总结了几点需要注意的地方,
1、实现Serializable接口的类才能够序列化,如果是父类实现了该接口,子类也可以进行序列化
这点不过多解释,规定就是这样。
2、静态成员不能被序列化、方法不能被序列化,关键字transient修饰的属性不能序列化
话不多说,上代码。
import java.io.*; /** * 测试静态成员、方法、transient修饰的属性不能序列化 */ public class SerializeTest { public static void main(String[] args) throws IOException { Test test = new Test(); // 创建被序列化的对象 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.data")); oos.writeObject(test); oos.close(); //这里记得序列化完成之后要关流,和io流一样,不多解释 } } class Test implements Serializable{ //创建一个类用于创建序列化对象 static int a; transient private int b; private int c; public void m(){} }
以上代码就是创建了一个类,然后对该类的对象进行序列化,序列化后的文件为test.data.然后查看了一个这个文件的大小为53字节。
下面我将这个类中的静态成员、transient修饰的成员和m()方法都注释掉再进行一次序列化
import java.io.*; /** * 测试静态成员、方法、transient修饰的属性不能序列化 */ public class SerializeTest { public static void main(String[] args) throws IOException { Test test = new Test(); // 创建被序列化的对象 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.data")); oos.writeObject(test); oos.close(); //这里记得序列化完成之后要关流,和io流一样,不多解释 } } class Test implements Serializable{ // 将static属性、transient属性和方法都注释掉之后在进行一次序列化 // static int a; // transient private int b; private int c; // public void m(){} }
此时,看到再序列化之后的文件大小依然还是53字节,但是如果再将成员变量c注释掉再进行序列化就会发现,这个文件变小了
由此可以说明,静态成员、transient修饰的成员和方法都不能序列化。
3、对于向上造型的对象进行序列化,实际序列化的对象是实际创建类
上代码
import java.io.*; /** * 测试向上造型的对象实际序列化的是实际创建类 */ public class SerializeTest { public static void main(String[] args) throws IOException, ClassNotFoundException { Test test = new Test02(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test02.data")); oos.writeObject(test); oos.close(); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test02.data")); System.out.println(ois.readObject()); //这里直接打印反序列化之后的对象信息 ois.close(); } } class Test implements Serializable{ //创建一个父类类用于创建序列化对象 } class Test02 extends Test{ // 子类继承父类 }
上面代码的执行结果:Test02@4f3f5b24,可以看到打印的就是子类的对象,so,这个就证明了向上造型的对象,实际序列化的是实际创建类,同时也证明了如果父类实现了Serializable接口,子类也是可以序列化滴。
4、对于已经序列化完的对象,在进行反序列化时,如果对原类信息进行了修改(包含不能被序列化的属性和方法),此时将不能进行反序列化
直接上代码。
import java.io.*; /** * 测试反序列化之前如果修改原类信息将不能进行反序列化 * */ public class SerializeTest { public static void main(String[] args) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test02.data")); System.out.println(ois.readObject()); //这里直接打印反序列化之后的对象信息 ois.close(); } } class Test implements Serializable{ //创建一个父类类用于创建序列化对象 } class Test02 extends Test{ // 子类继承父类 // test02.data这个文件就是第三条中创建对象序列化之后的文件 static int a = 1; //这里我随便添加了一个静态属性,然后执行代码 }
///下面是执行结果
Exception in thread "main" java.io.InvalidClassException: night.Test02; local class incompatible: stream classdesc serialVersionUID = -4461189770521473347, local class serialVersionUID = -7303293535763263875
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1829)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1713)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1986)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1535)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)
at night.SerializeTest.main(SerializeTest.java:23)
居然报了异常?
这里为什么会报异常呢,简单讲一哈:序列化的时候会计算一个序列化版本号-----serialVersionUID,并且将这个版本号一同存放到文件中,在反序列化的时候会获取此序列号,并且会计算当前类的serialVersionUID,如果两个版本号不相等就会报如上异常,反之反序列化就可以成功。~
那么对于这种情况就没有解决办法了? 当然是有的。
我们可以在类中直接声明serialVersionUID为 final属性,并让其自动赋初始值(这个值jvm肯定是计算过的了),格式如下:
private static final long serialVersionUID = 362498820763181265L;
这样不管怎样修改类的属性,这个serialVersionUID都不会改变,这样问题就解决了。
其实对于private static final long serialVersionUID = 362498820763181265L;这个东西,大家应该都见过的,比如我这里拿的就是HashMap类源码中的那个值。
以上就是浅析关于java的序列化和反序列化的详细内容,更多关于java序列化和反序列化的资料请关注脚本之家其它相关文章!