a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 150|回复: 0

[专业语言] Java认证辅导之关于Java对象序列化小结

[复制链接]
发表于 2012-8-4 12:44:44 | 显示全部楼层 |阅读模式
Java认证辅导之关于Java对象序列化小结
3 x2 ], d9 o. j' d+ fJava对象序列化小结! o' o0 ^1 R& ^. |$ s$ A
序列化的过程就是对象写入字节流和从字节流中读取对象。将对象状态转换成字节流之后,可以用java.io包中的各种字节流类将其保存到文件中,管道到另一线程中或通过网络连接将对象数据发送到另一主机。对象序列化功能非常简单、强大,在RMI、Socket、JMS、EJB都有应用。对象序列化问题在网络编程中并不是最激动人心的课题,但却相当重要,具有许多实用意义。# q% o; C1 K* V$ x# V4 k( B
对象序列化可以实现分布式对象。主要应用例如:RMI要利用对象序列化运行远程主机上的服务,就像在本地机上运行对象时一样。; a- j' K3 G! _+ l6 o
java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据。可以将整个对象层次写入字节流中,可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的“深复制”,即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。: q, i+ d: ]; M* X& Q: R' Z+ A0 Z- Y
从上面的叙述中,我们知道了对象序列化是java编程中的必备武器,那么让我们从基础开始,好好学习一下它的机制和用法。. f' o/ M# \4 c
java序列化比较简单,通常不需要编写保存和恢复对象状态的定制代码。实现java.io.Serializable接口的类对象可以转换成字节流或从字节流恢复,不需要在类中增加任何代码。只有极少数情况下才需要定制代码保存或恢复对象状态。这里要注意:不是每个类都可序列化,有些类是不能序列化的,例如涉及线程的类与特定JVM有非常复杂的关系。* b8 R3 o1 C; n: r+ _3 D* i: L
import java.io.FileInputStream;
3 F0 M* T4 p9 Q! l8 ]import java.io.FileOutputStream;
8 s- q) i0 h  ]  z# Timport java.io.IOException;4 ]* J8 G1 J4 R* t5 T. T$ i( k
import java.io.ObjectInputStream;0 M0 x$ G8 ]3 l. t. l2 v
import java.io.ObjectOutputStream;
6 i4 m6 F) u6 ^* S: @  rimport java.io.Serializable;
( I, K* A/ _% k7 Cpublic class SerialCtl implements Serializable {6 B+ }8 ~. }0 O
private String a;; k# C8 P9 ]6 O( u
private transient String b;7 @$ j7 J6 u# C% e) `
public SerialCtl(String aa, String bb) {
8 [  T1 Z+ v9 Xa = “Not Transient: ” + aa;/ e* I# q7 [4 K) `6 _5 P
b = “Transient: ” + bb;8 X# _" N; X* k# N
}+ t4 X6 D. ^: \5 m% i
public String toString() {
. a9 T7 j6 H# l  nreturn a + “ && ” + b;
% J" g! Q' z, P8 H" N$ z}! k& I5 l2 j1 N1 D- G7 K- z
private void writeObject(ObjectOutputStream stream) throws IOException {
3 Y8 O9 S5 p! _/ c; U7 k/ Estream.defaultWriteObject();
1 M+ y5 o8 g& V5 w- y: h1 @6 p* nstream.writeObject(b);//顺序有关
, c2 x) R, c- n) X% [stream.writeObject(“附加信息”);
7 C8 @' F: f' ^5 k4 H9 n3 w}: a- J! u$ j! A0 `9 V  B
private void readObject(ObjectInputStream stream) throws IOException,
# i- y: n4 U% h( @+ N' NClassNotFoundException {' S) C- o9 e# i
# O: l2 Y6 o* a! c

* M7 i( Y& g6 q: @stream.defaultReadObject();
+ x6 v/ x& P/ N9 K) A8 ]* Jb = (String) stream.readObject();//顺序有关
/ M9 g* @% @! y- G9 Z/ @System.out.println(b);% f5 n8 F. a2 i9 u' F  J5 D1 h
System.out.println((String) stream.readObject());
0 Z" p- o4 d. {8 n+ C/ F4 ]}: Y2 Y; U2 j4 i# o2 A/ v
public static void main(String[] args) throws IOException,
9 W& p1 A8 l3 z% g6 GClassNotFoundException {
! {8 {# M. Q  g, KSerialCtl sc = new SerialCtl(“Test1”, “Test2”);. l% U. J# a, b! i
System.out.println(“Before: ” + sc);
' q- ]5 Q; O% D( {) m& i( kFileOutputStream buf = new FileOutputStream(“x.tmp”);
, U& l! G+ W. x( A& y' w// ByteArrayOutputStream buf = new ByteArrayOutputStream();
( @/ R. C% K2 `9 u- r6 ~, \/ nObjectOutputStream o = new ObjectOutputStream(buf);
! a" }, j+ t* p5 c' Po.writeObject(sc);7 g1 c# f! e+ w( E5 F5 o5 o
o.flush();$ K  A; C) n* @/ I4 J
o.close();6 i: r9 S4 D# F8 O: w3 X
// Now get it back:' P; u% E2 h# ]% |0 s
ObjectInputStream in = new ObjectInputStream(new FileInputStream(“x.tmp”));
) H& g* e/ z+ K2 ~+ e$ U// ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buf.toByteArray()));
6 w! n; L/ t2 K+ \+ L! b9 OSerialCtl sc2 = (SerialCtl) in.readObject();
' M* z  i2 m3 |System.out.println(“After: ” + sc2);$ f$ o; e, C4 Z. V
}
8 h2 D8 p. f% L3 ]}
! _' c5 D6 y6 G$ @# u完全定制序列化过程:9 @# J4 U' ?2 L  D, f
如果一个类要完全负责自己的序列化,则实现Externalizable接口而不是Serializable接口。Externalizable接口定义包括两个方法writeExternal()与readExternal()。利用这些方法可以控制对象数据成员如何写入字节流。类实现Externalizable时,头写入对象流中,然后类完全负责序列化和恢复数据成员,除了头以外,根本没有自动序列化。这里要注意了。声明类实现Externalizable接口会有重大的安全风险。writeExternal()与readExternal()方法声明为public,恶意类可以用这些方法读取和写入对象数据。如果对象包含敏感信息,则要格外小心。这包括使用安全套接或加密整个字节流。
% [+ k' M3 y6 P- `0 I) ~; |假如你需要序列化一个类Test:/ c+ f7 @. i0 a
import java.io.*;  p- A- n8 U8 ]' z3 t1 V+ [3 E
class Test implements Externalizable{ //Test类必须实现Externalizable接口
! Z5 g' U7 ?; L& {$ [8 dprivate String letterstates = “fanruijun”;
1 D+ W/ \4 r; k& |. g# xprivate int num = 0;
1 ^1 j7 A& y% H% m" J, @public Test(){6 \8 {. e2 ~( m! u  I7 q7 z, Z" @# v9 J
}
5 A4 O5 R. |  o1 y: |" {+ Jpublic void writeExternal(ObjectOutput out) throws IOException {
# z) I! t0 `! Wout.writeObject(letterstates);
1 s) D9 ^2 J/ Eout.write(88); //在序列化的数据最后加个881 T, @; R, P" b) j; o2 ?. M9 e) S- w8 F
}' [+ C0 q: R5 K  Q( U7 B0 ~
public void readExternal(ObjectInput in) throws IOException,
  \/ m2 a. k. U7 EClassNotFoundException {& c( P1 x6 C& a! [/ q$ T- H
letterstates = (String)in.readObject();+ v1 H3 }3 P* Y& E
num = in.read(); //把数字88加进来; f# x4 H& f5 ], f' B( k
}0 I% \8 o+ u* b$ M. v& q' ~' ?
public void putOut(){ //测试2 A  J6 z, N; a0 S% G1 M
System.out.println(letterstates +num);( G6 h* I' o% {4 y6 c8 F, f
}
5 d  \/ b, ?/ e, ~$ k/ x1 W}! ^3 y# I" w7 d5 O) x* E; ~; [9 ^
序列化上面的Test类:AppTest# y. a! q! i& m, H; ?2 [. W% l" C$ G
import java.io.*;
' u* K% ?0 u& J! r4 vpublic class AppTest {4 d( o9 ^. X( p3 ]# v" [1 A
private void saveGame(){/ b( y3 {/ k0 x
Test m = new Test();
. F8 C# M- }, F8 Z4 cif (m != null){1 N* L& s, f$ i  D
try{
  r7 _; Q* ?' k  {! j. `FileOutputStream ostream = new FileOutputStream(“t.txt”);* F2 a0 [' M+ e. r
ObjectOutputStream p = new ObjectOutputStream(ostream);
, u0 Y5 q2 O( S9 G- Lp.writeObject(m); //writeExternal()自动执行# G! I2 ~1 g/ ?/ u
p.flush();% Z- F$ q( o1 O& B3 x7 b
ostream.close();
  Q3 U# R) P& L& m0 I} catch (IOException ioe) {) P. F$ W- p$ R2 {. r4 t! N2 L
System.out.println (“Error saving file:”);0 [: C' e' ~* \5 T
8 }* t+ C9 z0 K) Z% u
$ Z9 m( B7 Y+ h+ \1 o+ q
System.out.println (ioe.getMessage());( A5 p8 v2 n5 _9 J9 @, s
}4 L0 }! z& J5 Y; P( f& @) {3 y9 `
}
5 L  X8 W9 x" |  f! R3 e3 G}
3 Y$ H, N6 X2 b6 [private void loadGame(){
. E: \$ e; ?1 P% }$ Y/ y, A. Vtry{
3 o6 V2 @4 b) p/ e" c+ z2 CFileInputStream instream = new FileInputStream(“t.txt”);
7 _" G8 Q# [2 B& Y7 |) FObjectInputStream p = new ObjectInputStream(instream);
$ v0 E. y4 w/ g+ mTest m = (Test)p.readObject();//readExternal()自动执行
/ B+ o3 T; H9 ]5 t: xm.putOut();
$ i8 n7 @4 u/ j( }instream.close();% o  J" ?3 q% b  ^
} catch (Exception e) {$ S8 m( J. t+ `: C7 J% l
System.out.println (“Error loading file:”);
# b* I: q. [9 F2 X0 tSystem.out.println (e.getMessage());6 l5 s/ x; R' m5 z* y' M" l9 W# U
}9 j1 d1 d" C# }/ n& M7 H' _
}! K6 p! F6 X& g8 J) c
public static void main(String[] args){: @( u, W7 ~5 V6 i# z' E
new AppTest().saveGame();  m' j: S$ S4 g1 R8 W
new AppTest().loadGame();, q* @4 m1 l% h- `% P
}! Y6 t3 ^8 t7 s: b) U# |
}: c/ k4 R5 @! O% ]& h3 Q
serialVersionUID的作用
+ \- w1 W* O! Q1 y; ^设置 serialVersionUID默认的生成方式: private static final long serialVersionUID = 1L;0 X5 e  T6 N9 F$ s4 h) }3 D5 s% k9 q
serialVersionUID的作用:serialVersionUID 用来表明类的不同版本间的兼容性。如果你修改了此类, 要修改此值。否则以前用老版本的类序列化的类恢复时会出错。 在JDK中,可以利用JDK的bin目录下的serialver.exe工具产生这个serialVersionUID,对于Test.class,执行命令:serialver Test。$ I# b. E- p4 y! T: T; {, B
为了在反序列化时,确保类版本的兼容性,最好在每个要序列化的类中加入 private static final long serialVersionUID这个属性,具体数值自己定义。这样,即使某个类在与之对应的对象已经序列化出去后做了修改,该对象依然可以被正确反序列化。否则,如果不显式定义该属性,这个属性值将由JVM根据类的相关信息计算,而修改后的类的计算结果与修改前的类的计算结果往往不同,从而造成对象的反序列化因为类版本不兼容而失败。
/ d8 u7 A3 y1 e" ]& B9 T$ Q6 w8 t% Q不显式定义这个属性值的另一个坏处是,不利于程序在不同的JVM之间的移植。因为不同的编译器实现该属性值的计算策略可能不同,从而造成虽然类没有改变,但是因为JVM不同,出现因类版本不兼容而无法正确反序列化的现象出现。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-5-10 20:04 , Processed in 0.164620 second(s), 21 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表