java常见面试考点
往期文章推荐:
java常见面试考点(三十七):面向对象的三大特性
java常见面试考点(三十八):hashcode与equals
java常见面试考点(三十九):Math.round()方法
java常见面试考点(四十):try-catch-finally
java常见面试考点(四十一):short运算小错误
【版权申明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权);
本博客的内容来自于:java常见面试考点(四十二):序列化与反序列化;
学习、合作与交流联系q384660495;
本博客的内容仅供学习与参考,并非营利;
一、序列化与反序列化
在OSI七层协议模型中展现层(Presentation Layer)的主要功能是把应用层的对象转换成一段连续的二进制串,或者反过来,把二进制串转换成应用层的对象–这两个功能就是序列化和反序列化。一般而言,TCP/IP协议的应用层对应与OSI七层协议模型的应用层,展示层和会话层,所以序列化协议属于TCP/IP协议应用层的一部分。本文对序列化协议的讲解主要基于OSI七层协议模型。
序列化: 将数据结构或对象转换成二进制串的过程
反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程
序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化,可以对流化后的对象进行读写操作,也可以将流化后的对象传输与网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。
序列化就是把Java对象转为字节序列的过程;反序列化就是把字节序列恢复为Java对象的过程。对象序列化主要有两种用途:第一是把对象的字节序列保存到硬盘上,通常存放在一个文件中;再有一个作用就是在网络中传送对象的字节序列。持久化和网络传输
要实现序列化,需要将该类实现Serializable接口,该接口没有要实现的方法,实现Serializable接口只是为了标注该对象是可被序列化的,然后使用一个输出流(例如FileOutPutStream)来构造一个ObjectOutputStream(对象流)对象,接着使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则使用输入流。
基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种方式明显优于使用Object类的clone方法克隆对象。让问题在编译的时候暴露出来总好过在运行时才出现!
二、JAVA序列化中常见的问题
问题一:static 属性不能被序列化
原因:序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。
问题二:Transient 属性不会被序列化
我们在User里面加上一个transient 状态的心情属性mood;
public class User implements Serializable {
//年龄
private int age;
//名字
private String name;
//心情
private transient String mood;
//省略get set方法
}
把User对象设置值后写入文件
FileOutputStream fos = new FileOutputStream("D:\\temp.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
User user = new User();
user.setMood("愉快");
oos.writeObject(user);
oos.flush();
oos.close();
再把从文件读取出来的转换为对象并打印mood的值
FileInputStream fis = new FileInputStream("D:\\temp.txt");
ObjectInputStream oin = new ObjectInputStream(fis);
User user1 = (User) oin.readObject();
System.out.println("mood="+user1.getMood());
输出结果为:mood=null(原生类型为对应类型的默认值,包装类型为null)
问题三:序列化版本号serialVersionUID
所有实现序列化的对象都必须要有个版本号,这个版本号可以由我们自己定义,当我们没定义的时候JDK工具会按照我们对象的属性生成一个对应的版本号。
版本号有什么用?
其实这个版本号就和我们平常软件的版本号一样,你的软件版本号和官方的服务器版本不一致的话就告诉你有新的功能更新了,主要用于提示用户进行更新。序列化也一样,我们的对象通常需要根据业务的需求变化要新增、修改或者删除一些属性,在我们做了一些修改后,就通过修改版本号告诉
反序列化的那一方对象有了修改你需要同步修改。
使用JDK生成的版本号和我们自定义的版本号的区别?
JDK工具生成的serialVersionUID 是根据对象的属性信息生成的一个编号,这就意味着只要对象的属性有一点变动那么他的序列化版本号就会同步进行改变。
这种情况有时候就不太友好,就像我们的软件一样,使用JDK生成的serialVersionUID,只要对象有一丁点改变serialVersionUID就会随着变更,这样的话用户就得强制更新软件的版本,用户不更新就使用不了软件。
而大多数友好的情况也许是这样的,用户可以选择不更新,不更新的话用户只是无法体验新加的功能而已。
而这种方式就需要我们自定义的版本号了,这样我就可以在新增了属性后不修改serialVersionUID,反序列化的时候只是无法或许新加的属性,并不影响程序运行。
下面用代码测试一下我们的理论:
(1)对象属性序列化版本号不同进行序列化和反序列化
(2)对象新增属性,但是版本号相同也可以反序列化成功
(3)对象新增属性,但是版本号是使用的JDK生成序列化版本号
问题四:父类、子类序列化问题
序列化是以正向递归的形式进行的,如果父类实现了序列化那么其子类都将被序列化;子类实现了序列化而父类没实现序列化,那么只有子类的属性会进行序列化,而父类的属性是不会进行序列化的。
(1)父类没有实现序列化,子类实现序列化
执行结果,父类属性未被序列化:
(2)父类实现序列化,子类不实现序列化
执行结果,子类属性序列化正常
详细代码参考这篇文章:序列化理解起来很简单