a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 158|回复: 0

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

[复制链接]
发表于 2012-8-4 12:44:44 | 显示全部楼层 |阅读模式
Java认证辅导之关于Java对象序列化小结
" X. @" c3 K  y* a" a( ]Java对象序列化小结
# B" ?# A8 ^  l+ J. M7 A# X序列化的过程就是对象写入字节流和从字节流中读取对象。将对象状态转换成字节流之后,可以用java.io包中的各种字节流类将其保存到文件中,管道到另一线程中或通过网络连接将对象数据发送到另一主机。对象序列化功能非常简单、强大,在RMI、Socket、JMS、EJB都有应用。对象序列化问题在网络编程中并不是最激动人心的课题,但却相当重要,具有许多实用意义。
8 m" Y7 c* q6 X/ d- p# f$ x对象序列化可以实现分布式对象。主要应用例如:RMI要利用对象序列化运行远程主机上的服务,就像在本地机上运行对象时一样。
( I) E& f) F$ \java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据。可以将整个对象层次写入字节流中,可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的“深复制”,即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。5 D8 S9 k0 f' u
从上面的叙述中,我们知道了对象序列化是java编程中的必备武器,那么让我们从基础开始,好好学习一下它的机制和用法。
) K+ r6 r% [: c1 yjava序列化比较简单,通常不需要编写保存和恢复对象状态的定制代码。实现java.io.Serializable接口的类对象可以转换成字节流或从字节流恢复,不需要在类中增加任何代码。只有极少数情况下才需要定制代码保存或恢复对象状态。这里要注意:不是每个类都可序列化,有些类是不能序列化的,例如涉及线程的类与特定JVM有非常复杂的关系。  H9 i% V% A2 D( s: u+ Y& l
import java.io.FileInputStream;
7 m# w1 D9 G$ ?import java.io.FileOutputStream;
( C) p. W0 N! O2 M3 w# t  Oimport java.io.IOException;
$ C7 d! K; R' o- Mimport java.io.ObjectInputStream;
" ]/ m; W4 f. c9 r! x' v1 Mimport java.io.ObjectOutputStream;
* R+ v& Q: B6 ~+ E5 k' uimport java.io.Serializable;
5 j: f! E6 k1 Ypublic class SerialCtl implements Serializable {
8 R4 r" i/ f2 D/ r& Y9 fprivate String a;: s* N: M7 J+ U- ?0 e2 q+ a
private transient String b;
& q8 |! q4 ^/ kpublic SerialCtl(String aa, String bb) {! X2 A) X- i6 [( K1 u
a = “Not Transient: ” + aa;: C. {% ?6 Y7 ]0 c
b = “Transient: ” + bb;7 j9 L% r6 h9 e5 }
}
8 B4 a. H* u5 [) y5 }public String toString() {. T# N8 E, S2 R
return a + “ && ” + b;  f. V. V) A' \  Z: \; J
}
- w; M) s& d( B& `6 `; D, d* Rprivate void writeObject(ObjectOutputStream stream) throws IOException {
0 m( s- u% Y+ H$ c* J4 |. hstream.defaultWriteObject();% O5 J$ k3 F7 ~) R# M
stream.writeObject(b);//顺序有关
8 ~/ f6 Z. c2 |5 ~2 R( ustream.writeObject(“附加信息”);+ ?: t) x5 ^$ K& Y
}
8 F: D& ~% u2 d4 f4 f, ~5 cprivate void readObject(ObjectInputStream stream) throws IOException,0 |1 E" ]  D8 f; c
ClassNotFoundException {, M, ?" `8 H% {5 D) s

) k% V9 X9 t6 [0 j) A% Y" i2 a7 Z( v/ Y4 Z* E# S& Y2 b  W
stream.defaultReadObject();4 W* B# P; V% A. Q- D: ^
b = (String) stream.readObject();//顺序有关2 o  i' @3 u( S. a
System.out.println(b);# _9 p9 E( j0 T7 g7 X
System.out.println((String) stream.readObject());
4 a( Q9 i" s( f( ?1 K9 \' b}
4 |. {5 h) h7 Qpublic static void main(String[] args) throws IOException,
3 Q8 a- v5 [% u. s6 S3 b. XClassNotFoundException {
- Y1 w; q  q# p/ OSerialCtl sc = new SerialCtl(“Test1”, “Test2”);
( U, `! F1 j/ n0 w' TSystem.out.println(“Before: ” + sc);1 ?% c3 g! H6 o; p
FileOutputStream buf = new FileOutputStream(“x.tmp”);3 K, s2 y8 `. c! o, r
// ByteArrayOutputStream buf = new ByteArrayOutputStream();) T( E' o1 H  j- P) t/ _
ObjectOutputStream o = new ObjectOutputStream(buf);
+ M: S& c. z5 n' l9 n  j) bo.writeObject(sc);" }# v% V8 D  Q# W' V6 Z  B
o.flush();. v" _) c9 c7 t& a9 ~8 E
o.close();
) }1 N7 C6 I% a& M4 b// Now get it back:
$ T* p9 [1 n3 y) P; iObjectInputStream in = new ObjectInputStream(new FileInputStream(“x.tmp”));) S- G4 }% I% ^" u
// ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buf.toByteArray()));0 b6 R0 i( [: @% J" ^
SerialCtl sc2 = (SerialCtl) in.readObject();
! U4 [) j* e+ P/ T" i# QSystem.out.println(“After: ” + sc2);
% b. y2 [0 A: s6 A9 N}4 T$ _$ U3 l" ~( x0 y
}
- }$ z4 r, [; |9 ~$ H完全定制序列化过程:
5 N5 S( J, D" I0 N  D如果一个类要完全负责自己的序列化,则实现Externalizable接口而不是Serializable接口。Externalizable接口定义包括两个方法writeExternal()与readExternal()。利用这些方法可以控制对象数据成员如何写入字节流。类实现Externalizable时,头写入对象流中,然后类完全负责序列化和恢复数据成员,除了头以外,根本没有自动序列化。这里要注意了。声明类实现Externalizable接口会有重大的安全风险。writeExternal()与readExternal()方法声明为public,恶意类可以用这些方法读取和写入对象数据。如果对象包含敏感信息,则要格外小心。这包括使用安全套接或加密整个字节流。
- U" Q7 H1 X9 t* m$ p假如你需要序列化一个类Test:
2 D( `8 D! j" I# e$ V- Q( o8 O$ N& i, nimport java.io.*;/ h" H, f5 |; H
class Test implements Externalizable{ //Test类必须实现Externalizable接口
! @' u4 l& z! l' C. l5 rprivate String letterstates = “fanruijun”;
, D4 _4 F# ^8 C3 b/ C- |- Mprivate int num = 0;6 J; }2 z: y" d8 J- \9 S$ L
public Test(){2 v# r; I1 K8 B# G7 Z
}9 k% t- A0 t; `% ?( ]! I$ v) \
public void writeExternal(ObjectOutput out) throws IOException {
# l; Q2 `$ k7 i0 Vout.writeObject(letterstates);
7 `# L' w% M) Mout.write(88); //在序列化的数据最后加个88
, S* K$ o" o/ T) A+ o* x; z7 C}3 y% x- y/ b0 _. E6 A! a
public void readExternal(ObjectInput in) throws IOException,
, n1 U! v# j2 ^6 OClassNotFoundException {% P& W2 [" l4 w4 a
letterstates = (String)in.readObject();
) _& V; M) d. ?, ~+ \( }& onum = in.read(); //把数字88加进来
* p: n: F* f5 k- @}
, O& z8 i- G& r0 Y- Q# p; i0 B, v# mpublic void putOut(){ //测试
7 [. Q; C( ^3 ]2 K- k! USystem.out.println(letterstates +num);% R8 g) \+ c, c5 C
}
) {% p- ~* E& @}
4 {7 z9 t+ F% ~9 ?7 k序列化上面的Test类:AppTest
9 x/ m7 p8 V: g% yimport java.io.*;
% n4 u" [6 p, x  Bpublic class AppTest {+ ?  u- X5 G  v! P8 n  ^
private void saveGame(){1 @6 m3 e9 z( K% i; R5 S
Test m = new Test();
% L6 k8 i$ I0 _1 K. Q% O: V' lif (m != null){
' ?  @' W9 ]" Q- d" _try{
; _' C1 ?& l0 w9 n( h1 cFileOutputStream ostream = new FileOutputStream(“t.txt”);
5 O  a+ Y1 o0 eObjectOutputStream p = new ObjectOutputStream(ostream);$ c3 l6 P# Z& [* n
p.writeObject(m); //writeExternal()自动执行2 H" R' Y. Q% i! `9 U
p.flush();
* X5 s; Q. D: H5 Q8 M* ]ostream.close();' V7 k) b$ Z4 P5 m+ T; a
} catch (IOException ioe) {
; z) Q- |; R# m1 R5 x  bSystem.out.println (“Error saving file:”);1 H% J& f; T/ Q* ^# z" X0 r5 n

, r! S2 g1 K# v0 k6 T  q0 O9 w' [* W: S' M- K0 d; z9 u/ n
System.out.println (ioe.getMessage());
# _- K4 Y$ E7 T  q4 r9 ^}/ E6 x2 M3 w1 r5 e  N' N, ^% F
}* P- n9 n2 \! |+ N
}
0 y+ v5 i, F1 D$ y& [9 Hprivate void loadGame(){6 i  Y! _  x7 K- S$ D0 t1 B
try{
# W% a- n; p0 K% u' S- @FileInputStream instream = new FileInputStream(“t.txt”);
3 p) v) b4 [. k' l+ J: I+ f3 t! z! HObjectInputStream p = new ObjectInputStream(instream);
# m0 v, c9 T+ M# S: O7 T% H, u) ATest m = (Test)p.readObject();//readExternal()自动执行" p: |4 `/ G- c! w
m.putOut();2 i) a  ^8 f& p5 m
instream.close();
1 V& `! k* h+ l% o# t} catch (Exception e) {
) z; \7 M/ J& R2 d% \3 _System.out.println (“Error loading file:”);
* r, V1 \  G% a7 ~" ]System.out.println (e.getMessage());
1 ]$ O+ ?4 A# o4 t  E, o}
6 }8 z' [6 {2 T* W4 ~/ h0 @8 k! {}
6 N9 Q8 p* F2 Q* z! P$ B* {1 t/ mpublic static void main(String[] args){
0 c( x/ J; R  n  ]new AppTest().saveGame();
& P1 o- F) [* p3 Znew AppTest().loadGame();
1 R; V5 x) q# D4 q7 V+ n}
: n3 z6 v7 Q: r$ ^7 n  t}
" A& s8 J% E1 k: K, J" FserialVersionUID的作用' e* {3 G* u% M
设置 serialVersionUID默认的生成方式: private static final long serialVersionUID = 1L;6 J$ W6 I1 O( @, W5 e8 i9 C* l3 N
serialVersionUID的作用:serialVersionUID 用来表明类的不同版本间的兼容性。如果你修改了此类, 要修改此值。否则以前用老版本的类序列化的类恢复时会出错。 在JDK中,可以利用JDK的bin目录下的serialver.exe工具产生这个serialVersionUID,对于Test.class,执行命令:serialver Test。- m# F" \- I) g' W2 c
为了在反序列化时,确保类版本的兼容性,最好在每个要序列化的类中加入 private static final long serialVersionUID这个属性,具体数值自己定义。这样,即使某个类在与之对应的对象已经序列化出去后做了修改,该对象依然可以被正确反序列化。否则,如果不显式定义该属性,这个属性值将由JVM根据类的相关信息计算,而修改后的类的计算结果与修改前的类的计算结果往往不同,从而造成对象的反序列化因为类版本不兼容而失败。- M8 Q5 K1 G. x9 b
不显式定义这个属性值的另一个坏处是,不利于程序在不同的JVM之间的移植。因为不同的编译器实现该属性值的计算策略可能不同,从而造成虽然类没有改变,但是因为JVM不同,出现因类版本不兼容而无法正确反序列化的现象出现。
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-6-26 15:29 , Processed in 0.213245 second(s), 21 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

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