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类:

  1. public class Male {
  2. private String name;
  3. private int age;
  4. public Male() {
  5. }
  6. public String getName() {
  7. return name;
  8. }
  9. public void setName(String name) {
  10. this.name = name;
  11. }
  12. public int getAge() {
  13. return age;
  14. }
  15. public void setAge(int age) {
  16. this.age = age;
  17. }
  18. @Override
  19. public String toString() {
  20. return "Male [name=" + name + ", age=" + age + "]";
  21. }
  22. }

Male的子类MaleStudent则实现了Serializable接口:

  1. public class MaleStudent extends Male implements Serializable {
  2. private int studentID;
  3. public MaleStudent(int studentID) {
  4. super();
  5. this.studentID = studentID;
  6. }
  7. public int getStudentID() {
  8. return studentID;
  9. }
  10. public void setStudentID(int studentID) {
  11. this.studentID = studentID;
  12. }
  13. @Override
  14. public String toString() {
  15. return "MaleStudent [studentID=" + studentID + "]";
  16. }
  17. }

则反序列化MaleStudent对象后, 其name和age属性都发生了改变:

  1. MaleStudent student = new MaleStudent(1);
  2. student.setName("coolxing");
  3. student.setAge(24);
  4. ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(
  5. "male.obj"));
  6. out.writeObject(student);
  7. out.close();
  8. ObjectInputStream in = new ObjectInputStream(new FileInputStream(
  9. "male.obj"));
  10. MaleStudent maleStudent = (MaleStudent) in.readObject();
  11. System.out.println(maleStudent.getName() + ", " + maleStudent.getAge()
  12. + ", " + maleStudent.getStudentID());

程序的输出为:

name = null, age = 0, studentID = 1

可见, 父类属性值都"丢失"了, name和age都是调用Male无参构造函数之后的值. 假设将Male类中的无参构造函数删除, 再加上一个有参的构造函数, 在反序列化时将发生程序错误.
 
7. 自定义序列化和反序列化操作. 如果为了某些特殊需求, 需要自定义序列化和反序列化操作, 只要重写实现了Serializable接口的类中的writeObject()和readObject()方法即可, 典型的应用场景是对密码之类的敏感成员进行加密:
  1. public class User implements Serializable {
  2. private static final long serialVersionUID = 1L;
  3. private String userName;
  4. private String passWord;
  5. public User(String userName, String passWord) {
  6. this.userName = userName;
  7. this.passWord = passWord;
  8. }
  9. public User() {
  10. }
  11. private void writeObject(ObjectOutputStream out) {
  12. try {
  13. PutField field = out.putFields();
  14. field.put("userName", userName);
  15. System.out.println("加密前: passWord = " + passWord);
  16. // 模拟加密
  17. passWord = passWord + "1";
  18. System.out.println("加密后: passWord = " + passWord);
  19. field.put("passWord", passWord);
  20. out.writeFields();
  21. } catch (IOException e) {
  22. e.printStackTrace();
  23. }
  24. }
  25. private void readObject(ObjectInputStream in) {
  26. try {
  27. GetField field = in.readFields();
  28. userName = (String) field.get("userName", "");
  29. passWord = (String) field.get("passWord", "");
  30. System.out.println("读取的原始passWord = " + passWord);
  31. // 模拟解密
  32. passWord = passWord.substring(0, passWord.length() - 1);
  33. System.out.println("解密后的passWord = " + passWord);
  34. } catch (Exception e) {
  35. e.printStackTrace();
  36. }
  37. }
  38. }
程序的输出为:
加密前: passWord = 1987810
加密后: passWord = 19878101
读取的原始passWord = 19878101
解密后的passWord = 1987810
User [userName=coolxing, passWord=1987810]
 
8. 重复存储问题. 如果将同一个对象多次写入文件, 会有怎样的结果?
  1. File file = new File("user.obj");
  2. User user = new User("coolxing", "1987810");
  3. ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.obj"));
  4. out.writeObject(user);
  5. System.out.println(file.length());
  6. // 改变userName的值后再次将user对象存入文件
  7. user.setUserName("min");
  8. out.writeObject(user);
  9. System.out.println(file.length());
  10. ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.obj"));
  11. User userFromFile1 = (User) in.readObject();
  12. User userFromFile2 = (User) in.readObject();
  13. System.out.println(userFromFile1.toString());
  14. 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序列化机制的更多相关文章

  1. hadoop序列化机制与java序列化机制对比

    1.采用的方法: java序列化机制采用的ObjectOutputStream 对象上调用writeObject() 方法: Hadoop 序列化机制调用对象的write() 方法,带一个DataOu ...

  2. 输入和输出--java序列化机制

    对象的序列化 什么是Java对象的序列化? 对象序列化的目标是将对象保存到磁盘上,或允许在网络中直接传输对象.对象序列化机制允许把内存中的Java对象转换成与平台无关的二进制流,从而保存或者传输.其他 ...

  3. Java序列化机制和原理及自己的理解

    Java序列化算法透析 Serialization(序列化)是一种将对象以一连串的字节描述的过程:反序列化deserialization是一种将这些字节重建成一个对象的过程.Java序列化API提供一 ...

  4. Java序列化机制和原理

    Java序列化算法透析 Serialization(序列化)是一种将对象以一连串的字节描述的过程:反序列化deserialization是一种将这些字节重建成一个对象的过程.Java序列化API提供一 ...

  5. Java序列化机制剖析

    本文转载自longdick的博文<Java序列化算法透析>,原文地址:http://longdick.iteye.com Java序列化算法透析 Serialization(序列化)是一种 ...

  6. Java序列化机制原理

      Java序列化就是将一个对象转化为一个二进制表示的字节数组,通过保存或则转移这些二进制数组达到持久化的目的.要实现序列化,需要实现java.io.Serializable接口.反序列化是和序列化相 ...

  7. Java I/O系统学习系列五:Java序列化机制

    在Java的世界里,创建好对象之后,只要需要,对象是可以长驻内存,但是在程序终止时,所有对象还是会被销毁.这其实很合理,但是即使合理也不一定能满足所有场景,仍然存在着一些情况,需要能够在程序不运行的情 ...

  8. 细看Java序列化机制

    概况 在程序中为了能直接以 Java 对象的形式进行保存,然后再重新得到该 Java 对象,这就需要序列化能力.序列化其实可以看成是一种机制,按照一定的格式将 Java 对象的某状态转成介质可接受的形 ...

  9. java 序列化机制

    package stream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io ...

随机推荐

  1. JavaScript 之 document对象

    对象属性document.title //设置文档标题等价于HTML的title标签document.bgColor //设置页面背景色document.fgColor //设置前景色(文本颜色)do ...

  2. Codeforces Round #354 (Div. 2)-D

    D. Theseus and labyrinth 题目链接:http://codeforces.com/contest/676/problem/D Theseus has just arrived t ...

  3. SU susort命令学习

  4. hdu5438 Ponds dfs 2015changchun网络赛

    Ponds Time Limit: 1500/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Sub ...

  5. 获取iframe的元素并进行操作

    获取iframe中的document元素有一下集中方法: 1.getElementById()方法和contentWindow属性: window.onload=function(){ /*必须等待页 ...

  6. ACM Color the fence

    Color the fence 时间限制:1000 ms  |  内存限制:65535 KB 难度:2   描述 Tom has fallen in love with Mary. Now Tom w ...

  7. jquery validate学习心得

    据说,javascript最初的由来就是为了解决表单的验证问题,当然现在的js已经能够用来做各种各样炫酷的效果,不过表单验证一直以来都是js很重要的一个用途(在博客园第一篇博客,不知道说些什么开头~~ ...

  8. 【HDU】3853 LOOPS

    http://acm.hdu.edu.cn/showproblem.php?pid=3853 题意:n×m的格子,起始在(1,1),要求走到(n,m),在每一格(i,j)有三种走法,其中p[i,j,0 ...

  9. 编码Q&A

    Q:什么是编码? A:由于计算机中所有数据都是以二进制存在,那么为了存储数字,字母,各种符号和文字,计算机必须用一套映射系统来对应.比如我在某台计算机上规定,用00010001这个二进制数表示字母a, ...

  10. 你用java的swing可以做出这么炫的mp3播放器吗?

    这个mp3播放器是基于java的swing编写的,我认为界面还是可以拿出来和大家看一看评一评. 先说说创作的初衷,由于前段时间工作不是很忙,与其闲着,还不如找一些东西来给自己捣腾捣腾,在 之前写的 j ...