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

Java代码

publicclassMale?{

privateString?name;

privateintage;

publicMale()?{

}

publicString?getName()?{

returnname;

}

publicvoidsetName(String?name)?{

this.name?=?name;

}

publicintgetAge()?{

returnage;

}

publicvoidsetAge(intage)?{

this.age?=?age;

}

@Override

publicString?toString()?{

return"Male?[name="+?name?+",?age="+?age?+"]";

}

}

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

Java代码

publicclassMaleStudentextendsMaleimplementsSerializable?{

privateintstudentID;

publicMaleStudent(intstudentID)?{

super();

this.studentID?=?studentID;

}

publicintgetStudentID()?{

returnstudentID;

}

publicvoidsetStudentID(intstudentID)?{

this.studentID?=?studentID;

}

@Override

publicString?toString()?{

return"MaleStudent?[studentID="+?studentID?+"]";

}

}

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

Java代码

MaleStudent?student?=newMaleStudent(1);

student.setName("coolxing");

student.setAge(24);

ObjectOutputStream?out?=newObjectOutputStream(newFileOutputStream(

"male.obj"));

out.writeObject(student);

out.close();

ObjectInputStream?in?=newObjectInputStream(newFileInputStream(

"male.obj"));

MaleStudent?maleStudent?=?(MaleStudent)?in.readObject();

System.out.println(maleStudent.getName()?+",?"+?maleStudent.getAge()

+",?"+?maleStudent.getStudentID());

程序的输出为:

name = null, age = 0, studentID = 1

可见, 父类属性值都"丢失"了, name和age都是调用Male无参构造函数之后的值. 假设将Male类中的无参构造函数删除, 再加上一个有参的构造函数,在反序列化时将发生程序错误.

7. 自定义序列化和反序列化操作. 如果为了某些特殊需求, 需要自定义序列化和反序列化操作, 只要重写实现了Serializable接口的类中的writeObject()和readObject()方法即可, 典型的应用场景是对密码之类的敏感成员进行加密:

Java代码

publicclassUserimplementsSerializable?{

privatestaticfinallongserialVersionUID?=?1L;

privateString?userName;

privateString?passWord;

publicUser(String?userName,?String?passWord)?{

this.userName?=?userName;

this.passWord?=?passWord;

}

publicUser()?{

}

privatevoidwriteObject(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();

}

}

privatevoidreadObject(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();

}

}

}

程序的输出为:

加密前: passWord = 1987810

加密后: passWord = 19878101

读取的原始passWord = 19878101

解密后的passWord = 1987810

User [userName=coolxing, passWord=1987810]

8. 重复存储问题. 如果将同一个对象多次写入文件, 会有怎样的结果?

Java代码

File?file?=newFile("user.obj");

User?user?=newUser("coolxing","1987810");

ObjectOutputStream?out?=newObjectOutputStream(newFileOutputStream("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?=newObjectInputStream(newFileInputStream("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接口呢? 这是我疑惑的地方, 文档中并没有说明.可以确定的是,

只要集合中的元素是可序列化的, 序列化过程就不会出错.

最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351

推荐阅读更多精彩内容