Java序列化机制
java的序列化机制支持将对象序列化为本地文件或者通过网络传输至别处, 而反序列化则可以读取流中的数据, 并将其转换为java对象. 被序列化的类需要实现Serializable接口, 使用ObjectInputStream和ObjectOutputStream进行对象的读写操作.
当然, java的序列化机制并非如此简单, 以下是个人总结的一些知识点:
1. 对象读取的顺序应该和写入的顺序一致, 而且读取的次数不能超过已写入对象的个数. 比如文件中仅仅存在2个对象, 就不能连续调用3次readObject()方法, 除非调用了reset, skip等对流重新定位的方法.
2. java序列化机制针对的是对象, 而不是类. 因此只有非静态成员变量才会被序列化成二进制数据.
3. 使用transient关键字修饰的成员变量不会被序列化为二进制数据.
4. 将对象序列化为二进制数据, 将二进制数据反序列化为java对象, 这两个操作可能位于不同的应用中, 甚至也可能在不同的计算机上进行. 需要保证这两种场合下都有class文件, 在序列化处和反序列处的class文件需要完成一致, 包括包名.
5. 序列化ID的作用. 上面的Person类中定义了序列化ID: private static final long serialVersionUID = 1L;
这是一个非强制定义的静态成员, 如果不定义序列化ID, 那么eclipse会给出一个黄色的警告, 这个警告可以忽略.
考虑这样的情形: Person类定义了序列化ID, 且序列化对象时serialVersionUID的值为1, 而反序列化时serialVersionUID的值不为1, 那么此时将无法反序列化成功. 所以序列化ID可以用来限制某些用户的反序列化.
6. 父类的序列化问题. 根据java的对象实例化机制可知, 创建一个子类对象的过程中, 会创建其父类对象, 反序列化也不例外. 如果一个类实现了Serializable接口, 而其父类却没有实现Serializable接口, 那么在反序列化时, 虚拟机会调用父类的无参构造函数创建父类对象, 因此反序列化后父类成员变量的值为调用无参构造函数之后的值. 如果父类既没有实现Serializable接口, 也不存在无参构造函数, 那么在反序列化时将发生程序错误.
假设存在一个没有实现Serializable接口的Male类:
- public class Male {
- private String name;
- private int age;
- public Male() {
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- @Override
- public String toString() {
- return "Male [name=" + name + ", age=" + age + "]";
- }
- }
Male的子类MaleStudent则实现了Serializable接口:
- public class MaleStudent extends Male implements Serializable {
- private int studentID;
- public MaleStudent(int studentID) {
- super();
- this.studentID = studentID;
- }
- public int getStudentID() {
- return studentID;
- }
- public void setStudentID(int studentID) {
- this.studentID = studentID;
- }
- @Override
- public String toString() {
- return "MaleStudent [studentID=" + studentID + "]";
- }
- }
则反序列化MaleStudent对象后, 其name和age属性都发生了改变:
- MaleStudent student = new MaleStudent(1);
- student.setName("coolxing");
- student.setAge(24);
- ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(
- "male.obj"));
- out.writeObject(student);
- out.close();
- ObjectInputStream in = new ObjectInputStream(new FileInputStream(
- "male.obj"));
- MaleStudent maleStudent = (MaleStudent) in.readObject();
- System.out.println(maleStudent.getName() + ", " + maleStudent.getAge()
- + ", " + maleStudent.getStudentID());
程序的输出为:
name = null, age = 0, studentID = 1
- public class User implements Serializable {
- private static final long serialVersionUID = 1L;
- private String userName;
- private String passWord;
- public User(String userName, String passWord) {
- this.userName = userName;
- this.passWord = passWord;
- }
- public User() {
- }
- private void writeObject(ObjectOutputStream out) {
- try {
- PutField field = out.putFields();
- field.put("userName", userName);
- System.out.println("加密前: passWord = " + passWord);
- // 模拟加密
- passWord = passWord + "1";
- System.out.println("加密后: passWord = " + passWord);
- field.put("passWord", passWord);
- out.writeFields();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- private void readObject(ObjectInputStream in) {
- try {
- GetField field = in.readFields();
- userName = (String) field.get("userName", "");
- passWord = (String) field.get("passWord", "");
- System.out.println("读取的原始passWord = " + passWord);
- // 模拟解密
- passWord = passWord.substring(0, passWord.length() - 1);
- System.out.println("解密后的passWord = " + passWord);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- File file = new File("user.obj");
- User user = new User("coolxing", "1987810");
- ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.obj"));
- out.writeObject(user);
- System.out.println(file.length());
- // 改变userName的值后再次将user对象存入文件
- user.setUserName("min");
- out.writeObject(user);
- System.out.println(file.length());
- ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.obj"));
- User userFromFile1 = (User) in.readObject();
- User userFromFile2 = (User) in.readObject();
- System.out.println(userFromFile1.toString());
- System.out.println(userFromFile2.toString());
程序的输出结果为:
109
114
User [userName=coolxing, passWord=1987810]
User [userName=coolxing, passWord=1987810]
程序中调用了2次readObject()方法, 且没有出现程序错误, 由此可知确实向文件中写入了2个对象. 第二次写入user对象时, 系统发现文件中已经存在user对象, 将不再存入user对象的内容, 只写入一个引用和一些控制信息, 所以第二次写入user对象后文件的大小增加的很少, 而且userName也没有发生改变.
9. 如果一个类中包含非基本数据类型的成员变量, 那么不仅类本身需要实现Serializable接口, 类中的非基本数据类型也需要实现Serializable接口. java的一些核心类, 如String,
基本数据类型的包装类等都已经实现了Serializable接口, 使用的时候可以查看文档.
10. 对于包含集合型成员的类来说, 不仅类本身需要现Serializable接口, 集合中所存储的元素也要实现Serializable接口.
那么集合类(List, Set, Map)到底有没有实现Serializable接口呢? 这是我疑惑的地方, 文档中并没有说明. 可以确定的是,
只要集合中的元素是可序列化的, 序列化过程就不会出错.
原文地址:http://coolxing.iteye.com/blog/1222783
Java序列化机制的更多相关文章
- hadoop序列化机制与java序列化机制对比
1.采用的方法: java序列化机制采用的ObjectOutputStream 对象上调用writeObject() 方法: Hadoop 序列化机制调用对象的write() 方法,带一个DataOu ...
- 输入和输出--java序列化机制
对象的序列化 什么是Java对象的序列化? 对象序列化的目标是将对象保存到磁盘上,或允许在网络中直接传输对象.对象序列化机制允许把内存中的Java对象转换成与平台无关的二进制流,从而保存或者传输.其他 ...
- Java序列化机制和原理及自己的理解
Java序列化算法透析 Serialization(序列化)是一种将对象以一连串的字节描述的过程:反序列化deserialization是一种将这些字节重建成一个对象的过程.Java序列化API提供一 ...
- Java序列化机制和原理
Java序列化算法透析 Serialization(序列化)是一种将对象以一连串的字节描述的过程:反序列化deserialization是一种将这些字节重建成一个对象的过程.Java序列化API提供一 ...
- Java序列化机制剖析
本文转载自longdick的博文<Java序列化算法透析>,原文地址:http://longdick.iteye.com Java序列化算法透析 Serialization(序列化)是一种 ...
- Java序列化机制原理
Java序列化就是将一个对象转化为一个二进制表示的字节数组,通过保存或则转移这些二进制数组达到持久化的目的.要实现序列化,需要实现java.io.Serializable接口.反序列化是和序列化相 ...
- Java I/O系统学习系列五:Java序列化机制
在Java的世界里,创建好对象之后,只要需要,对象是可以长驻内存,但是在程序终止时,所有对象还是会被销毁.这其实很合理,但是即使合理也不一定能满足所有场景,仍然存在着一些情况,需要能够在程序不运行的情 ...
- 细看Java序列化机制
概况 在程序中为了能直接以 Java 对象的形式进行保存,然后再重新得到该 Java 对象,这就需要序列化能力.序列化其实可以看成是一种机制,按照一定的格式将 Java 对象的某状态转成介质可接受的形 ...
- java 序列化机制
package stream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io ...
随机推荐
- js动画实现侧边栏分享
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content ...
- 树状数组+STL FZU 2029 买票问题
题目传送门 题意:中文题面 分析:隔了一个考试周再做,开始没有什么思路,感觉能用线段树/树状数组维护,树状数组维护最小值不会去写线段树,结果超时.后来发现只要维护前缀几个人以及用优先队列/set维护最 ...
- Eclipse快捷键/快捷操作汇总
1.建立.切换不同的工作空间: 工作空间是放置项目的,它是项目的集合,多个工程放在一个工作空间上容易出问题,建议把不同项目存放在单独的工作 空间内,让项目代码更加有序 file → switch w ...
- qq邮箱邮我组件
http://openmail.qq.com/cgi-bin/qm_help_mailme?sid=uvkgSu7e0aOrc0Qc&t=open_mailme 邮我 使用"邮我&q ...
- 每天一个linux命令---netstat
中间件访问第三方服务,经常出现连不上的情况.可以增加监控,当出现异常的时候触发一些动作通知程序员 例如: 要在app 部署的主机上,应该登录 172.16.210.52 后运行 netstat - ...
- 如何处理js的跨域问题
在bill.mail.10086.cn域内访问smsrebuild1.mail.10086.cn下的接口出现“阻止跨域源请求” 例如: URL 说明 是否允许通信 http://www.a.com/a ...
- Visual Studio: How to change ipch path in Visual Studio 2010 (.sdf, *.opensdf, ...)
Link: http://stackoverflow.com/questions/4315681/how-to-change-ipch-path-in-visual-studio-2010 引用: T ...
- (转)Ubuntu samba配置服务文件包
转:http://os.51cto.com/art/201001/176366.htm Ubuntu samba服务配置是很不错的文件应用很有学习价值,这里我主要讲解Ubuntu samba服务配置的 ...
- connect & express简介
Node.js[5] connect & express简介 Node.js[4] 第一个模块 Node.js[3] 俯瞰API (整理中) Node.js[2] Hello Node Nod ...
- [ JS 进阶 ] test, exec, match, replace
https://segmentfault.com/a/1190000003497780 对了,这篇文章可能会涉及到正则表达式相关知识,所以推荐没有正则基础的去看看这篇入门文章:正则表达式30分钟入门教 ...