javaandroid 实体类序列化化

如果一个类的超类没有实现Serializable接口,那么该类能否序列化?【java吧】_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:648,912贴子:
如果一个类的超类没有实现Serializable接口,那么该类能否序列化?收藏
兄弟连专业java培训,知名畅销书籍「明哥聊求职」等出品机构,11年专注培养学员受益java之道,跟兄弟连学java,学不会不就业才是奇迹!
求大神,求解释,各种求。。。。。
这个父类的域
怎么理解?
我的理解大概是这样如果一个可序列话的子类的父类不能可序列话,但必须有个无参构造器。(有么可序列话)不然会抛出InvalidClassException异常。如果只有无参构造器,那父类的定义的成员变量就不会序列化到二进制流中所以子类必须负责序列化(反序列化)父类的成员变量(Field)
登录百度帐号推荐应用序列化的几种方式 - 序列化,protobuf,serialize - Java - ITeye论坛
序列化的几种方式
锁定老帖子
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
来自: 北京
发表时间:&&
相关知识库:
在java中socket传输数据时,数据类型往往比较难选择。可能要考虑带宽、跨语言、版本的兼容等问题。比较常见的做法有两种:一是把对象包装成JSON字符串传输,二是采用java对象的序列化和反序列化。随着Google工具protoBuf的开源,protobuf也是个不错的选择。对JSON,Object Serialize,ProtoBuf 做个对比。
定义一个待传输的对象UserVo:
public class UserVo{
private List&UserVo&
初始化UserVo的实例src:
UserVo src = new UserVo();
src.setName("Yaoming");
src.setAge(30);
src.setPhone(L);
UserVo f1 = new UserVo();
f1.setName("tmac");
f1.setAge(32);
f1.setPhone(L);
UserVo f2 = new UserVo();
f2.setName("liuwei");
f2.setAge(29);
f2.setPhone(L);
List&UserVo& friends = new ArrayList&UserVo&();
friends.add(f1);
friends.add(f2);
src.setFriends(friends);
采用Google的gson-2.2.2.jar 进行转义
Gson gson = new Gson();
String json = gson.toJson(src);
得到的字符串:
{"name":"Yaoming","age":30,"phone":,"friends":[{"name":"tmac","age":32,"phone":},{"name":"liuwei","age":29,"phone":}]}
字节数为153
Json的优点:明文结构一目了然,可以跨语言,属性的增加减少对解析端影响较小。缺点:字节数过多,依赖于不同的第三方类库。
Object Serialize
UserVo实现Serializalbe接口,提供唯一的版本号:
public class UserVo implements Serializable{
private static final long serialVersionUID = -8742258L;
private List&UserVo&
序列化方法:
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bos);
os.writeObject(src);
os.flush();
os.close();
byte[] b = bos.toByteArray();
bos.close();
字节数是238
反序列化:
ObjectInputStream ois = new ObjectInputStream(fis);
vo = (UserVo) ois.readObject();
ois.close();
fis.close();
Object Serializalbe 优点:java原生支持,不需要提供第三方的类库,使用比较简单。缺点:无法跨语言,字节数占用比较大,某些情况下对于对象属性的变化比较敏感。
对象在进行序列化和反序列化的时候,必须实现Serializable接口,但并不强制声明唯一的serialVersionUID
是否声明serialVersionUID对于对象序列化的向上向下的兼容性有很大的影响。我们来做个测试:
把UserVo中的serialVersionUID去掉,序列化保存。反序列化的时候,增加或减少个字段,看是否成功。
public class UserVo implements Serializable{
private List&UserVo&
保存到文件中:
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bos);
os.writeObject(src);
os.flush();
os.close();
byte[] b = bos.toByteArray();
bos.close();
FileOutputStream fos = new FileOutputStream(dataFile);
fos.write(b);
fos.close();
增加或者减少字段后,从文件中读出来,反序列化:
FileInputStream fis = new FileInputStream(dataFile);
ObjectInputStream ois = new ObjectInputStream(fis);
vo = (UserVo) ois.readObject();
ois.close();
fis.close();
结果:抛出异常信息
Exception in thread "main" java.io.InvalidClassException: serialize.obj.UserVo; local class incompatible: stream classdesc serialVersionUID = 1390189, local class serialVersionUID = 7432394
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:560)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1582)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1495)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1731)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
at serialize.obj.ObjectSerialize.read(ObjectSerialize.java:74)
at serialize.obj.ObjectSerialize.main(ObjectSerialize.java:27)
eclipse指定生成一个serialVersionUID,序列化保存,修改字段后反序列化
结果:反序列化成功
如果没有明确指定serialVersionUID,序列化的时候会根据字段和特定的算法生成一个serialVersionUID,当属性有变化时这个id发生了变化,所以反序列化的时候就会失败。抛出“本地classd的唯一id和流中class的唯一id不匹配”。
jdk文档关于serialVersionUID的描述:
如果可序列化类未显式声明 serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认 serialVersionUID 值,如“Java(TM) 对象序列化规范”中所述。不过,强烈建议 所有可序列化类都显式声明 serialVersionUID 值,原因是计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的 InvalidClassException。因此,为保证 serialVersionUID 值跨不同 java 编译器实现的一致性,序列化类必须声明一个明确的 serialVersionUID 值。还强烈建议使用 private 修饰符显示声明 serialVersionUID(如果可能),原因是这种声明仅应用于直接声明类 -- serialVersionUID 字段作为继承成员没有用处。数组类不能声明一个明确的 serialVersionUID,因此它们总是具有默认的计算值,但是数组类没有匹配 serialVersionUID 值的要求。
Google ProtoBuf
protocol buffers 是google内部得一种传输协议,目前项目已经开源(/p/protobuf/)。它定义了一种紧凑得可扩展得二进制协议格式,适合网络传输,并且针对多个语言有不同得版本可供选择。
以protobuf-2.5.0rc1为例,准备工作:
下载源码,解压,编译,安装
tar zxvf protobuf-2.5.0rc1.tar.gz
./configure
./make install
MacBook-Air:~ ming$ protoc --version
libprotoc 2.5.0
安装成功!进入源码得java目录,用mvn工具编译生成所需得jar包,protobuf-java-2.5.0rc1.jar
1、编写.proto文件,命名UserVo.proto
option java_package = "serialize";
option java_outer_classname="UserVoProtos";
message UserVo{
optional string name = 1;
optional int32 age = 2;
optional int64 phone = 3;
repeated serialize.UserVo friends = 4;
2、在命令行利用protoc 工具生成builder类
protoc -IPATH=.proto文件所在得目录 --java_out=java文件的输出路径
.proto的名称
得到UserVoProtos类
3、编写序列化代码
UserVoProtos.UserVo.Builder builder = UserVoProtos.UserVo.newBuilder();
builder.setName("Yaoming");
builder.setAge(30);
builder.setPhone(L);
UserVoProtos.UserVo.Builder builder1 = UserVoProtos.UserVo.newBuilder();
builder1.setName("tmac");
builder1.setAge(32);
builder1.setPhone(L);
UserVoProtos.UserVo.Builder builder2 = UserVoProtos.UserVo.newBuilder();
builder2.setName("liuwei");
builder2.setAge(29);
builder2.setPhone(L);
builder.addFriends(builder1);
builder.addFriends(builder2);
UserVoProtos.UserVo vo = builder.build();
byte[] v = vo.toByteArray();
4、反序列化
UserVoProtos.UserVo uvo = UserVoProtos.UserVo.parseFrom(dstb);
System.out.println(uvo.getFriends(0).getName());
结果:tmac,反序列化成功
google protobuf 优点:字节数很小,适合网络传输节省io,跨语言 。缺点:需要依赖于工具生成代码。
proto文件是对数据的一个描述,包括字段名称,类型,字节中的位置。protoc工具读取proto文件生成对应builder代码的类库。protoc xxxxx
--java_out=xxxxxx 生成java类库。builder类根据自己的算法把数据序列化成字节流,或者把字节流根据反射的原理反序列化成对象。官方的示例:/protocol-buffers/docs/javatutorial。
proto文件中的字段类型和java中的对应关系:
详见:/protocol-buffers/docs/proto
.proto Type
字段属性的描述:
required: a well-formed message must have exactly one of this field.optional: a well-formed message can have zero or one of this field (but not more than one).repeated: this field can be repeated any number of times (including zero) in a well-formed message. The order of the repeated values will be preserved.
protobuf 在序列化和反序列化的时候,是依赖于.proto文件生成的builder类完成,字段的变化如果不表现在.proto文件中就不会影响反序列化,比较适合字段变化的情况。做个测试:
把UserVo序列化到文件中:
UserVoProtos.UserVo vo = builder.build();
byte[] v = vo.toByteArray();
FileOutputStream fos = new FileOutputStream(dataFile);
fos.write(vo.toByteArray());
fos.close();
为UserVo增加字段,对应的.proto文件:
option java_package = "serialize";
option java_outer_classname="UserVoProtos";
message UserVo{
optional string name = 1;
optional int32 age = 2;
optional int64 phone = 3;
repeated serialize.UserVo friends = 4;
optional string address = 5;
从文件中反序列化回来:
FileInputStream fis = new FileInputStream(dataFile);
byte[] dstb = new byte[fis.available()];
for(int i=0;i&dstb.i++){
dstb[i] = (byte)fis.read();
fis.close();
UserVoProtos.UserVo uvo = UserVoProtos.UserVo.parseFrom(dstb);
System.out.println(uvo.getFriends(0).getName());
成功得到结果。
三种方式对比传输同样的数据,google protobuf只有53个字节是最少的。结论:
跨语言、格式清晰一目了然
字节数比较大,需要第三方类库
Object Serialize
java原生方法不依赖外部类库
字节数比较大,不能跨语言
Google protobuf
跨语言、字节数比较少
编写.proto配置用protoc工具生成对应的代码
以上测试用例覆盖面比较窄,可能无法正确反应真实情况仅代表个人观点,欢迎随时指正和讨论。
等级: 初级会员
来自: 广州
发表时间:&&
好久没在这里看过这么好的文章了,谢谢分享。
请登录后投票
梵净鸢尾兰
等级: 初级会员
来自: 北京
发表时间:&&
受益匪浅,值得学习
请登录后投票
等级: 初级会员
来自: 南京
发表时间:&&
个人比较喜欢JSON,不过protobuf这个新技术值得学习~
请登录后投票
等级: 初级会员
来自: 杭州
发表时间:&&
吸收个新技术...
请登录后投票
等级: 初级会员
来自: 北京
发表时间:&&
很好不错,但是楼主有没有想过,通过自定义序列化方法,实现比java原生效果更好的结果
请登录后投票
等级: 初级会员
来自: 南京
发表时间:&&
viano 写道很好不错,但是楼主有没有想过,通过自定义序列化方法,实现比java原生效果更好的结果
具体一点呢,觉得用json就可以做到了。
请登录后投票
等级: 初级会员
来自: 北京
发表时间:&&
还没有说xml了..不过不重要了..我觉得应该吧序列化.和反序列化速度做个对比就更好了
请登录后投票
跳转论坛:移动开发技术
Web前端技术
Java企业应用
编程语言技术2013年12月 Web 开发大版内专家分月排行榜第三
本帖子已过去太久远了,不再提供回复功能。Java序列化1:序列化、反序列化和transient关键字的作用
时间: 21:15:44
&&&& 阅读:869
&&&& 评论:
&&&& 收藏:0
标签:网上讲Java序列化的文章很多,感觉很多都讲得不全,这篇文章希望可以全面地剖析Java的序列化机制。为什么要进行序列化和反序列化?我们写了一个Object,但那是Java虚拟机堆内存里面的东西,利用Object进行网络通信、IO操作的时候怎么会认识Java堆内存里面的东西?所以,需要序列化和反序列化机制的保障。
序列化:将一个对象转换成一串二进制表示的字节数组,通过保存或转移这些字节数据来达到持久化的目的。
反序列化:将字节数组重新构造成对象。
默认序列化
序列化只需要实现java.io.Serializable接口就可以了。序列化的时候有一个serialVersionUID参数,Java序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化,Java虚拟机会把传过来的字节流中的serialVersionUID和本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的实体类,可以进行反序列化,否则Java虚拟机会拒绝对这个实体类进行反序列化并抛出异常。serialVersionUID有两种生成方式:
1、默认的1L
2、根据类名、接口名、成员方法以及属性等来生成一个64位的Hash字段
如果实现java.io.Serializable接口的实体类没有显式定义一个名为serialVersionUID、类型为long的变量时,Java序列化机制会根据编译的.class文件自动生成一个serialVersionUID,如果.class文件没有变化,那么就算编译再多次,serialVersionUID也不会变化。换言之,Java为用户定义了默认的序列化、反序列化方法,其实就是ObjectOutputStream的defaultWriteObject方法和ObjectInputStream的defaultReadObject方法。看一个例子:
1 public class SerializableObject implements Serializable
private static final long serialVersionUID = 1L;
private String str0;
private transient String str1;
private static String str2 = "abc";
public SerializableObject(String str0, String str1)
this.str0 = str0;
this.str1 = str1;
public String getStr0()
return str0;
public String getStr1()
return str1;
1 public static void main(String[] args) throws Exception
File file = new File("D:" + File.separator + "s.txt");
OutputStream os = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(new SerializableObject("str0", "str1"));
oos.close();
InputStream is = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(is);
SerializableObject so = (SerializableObject)ois.readObject();
System.out.println("str0 = " + so.getStr0());
System.out.println("str1 = " + so.getStr1());
ois.close();
先不运行,用一个二进制查看器查看一下s.txt这个文件,并详细解释一下每一部分的内容。
第1部分是序列化文件头
◇AC ED:STREAM_MAGIC序列化协议
◇00 05:STREAM_VERSION序列化协议版本
◇73:TC_OBJECT声明这是一个新的对象
第2部分是要序列化的类的描述,在这里是SerializableObject类
◇72:TC_CLASSDESC声明这里开始一个新的class
◇00 1F:十进制的31,表示class名字的长度是31个字节
◇63 6F 6D ... 65 63 74:表示的是&com.xrq.test.SerializableObject&这一串字符,可以数一下确实是31个字节
◇00 00 00 00 00 00 00 01:SerialVersion,序列化ID,1
◇02:标记号,声明该对象支持序列化
◇00 01:该类所包含的域的个数为1个
第3部分是对象中各个属性项的描述
◇4C:这个没弄清楚意思
◇00 04:十进制的4,表示属性名的长度
◇73 74 72 30:字符串&str0&,属性名
◇74:这个没弄清楚意思
第4部分是该对象父类的信息,如果没有父类就没有这部分。有父类和第2部分差不多
◇00 12:十进制的18,表示父类的长度
◇4C 6A 61 ... 6E 67 3B:&L/java/lang/S&表示的是父类属性
◇78:TC_ENDBLOCKDATA,对象块结束的标志
◇74:TC_NULL,说明没有其他超类的标志
第5部分输出对象的属性项的实际值,如果属性项是一个对象,这里还将序列化这个对象,规则和第2部分一样
◇00 04:十进制的4,属性的长度
◇73 74 72 30:字符串&str0&,str0的属性值
从以上对于序列化后的二进制文件的解析,我们可以得出以下几个关键的结论:
1、序列化之后保存的是类的信息
2、被声明为transient的属性不会被序列化,这就是transient关键字的作用
3、被声明为static的属性不会被序列化,这个问题可以这么理解,序列化保存的是对象的状态,但是static修饰的变量是属于类的而不是属于变量的,因此序列化的时候不会序列化它
接下来运行一下上面的代码看一下
str0 = str0
str1 = null
因为str1是一个transient类型的变量,没有被序列化,因此反序列化出来也是没有任何内容的,显示的null,符合我们的结论。
手动指定序列化过程
Java并不强求用户非要使用默认的序列化方式,用户也可以按照自己的喜好自己指定自己想要的序列化方式----只要你自己能保证序列化前后能得到想要的数据就好了。手动指定序列化方式的规则是:
进行序列化、反序列化时,虚拟机会首先试图调用对象里的writeObject和readObject方法,进行用户自定义的序列化和反序列化。如果没有这样的方法,那么默认调用的是ObjectOutputStream的defaultWriteObject以及ObjectInputStream的defaultReadObject方法。换言之,利用自定义的writeObject方法和readObject方法,用户可以自己控制序列化和反序列化的过程。
这是非常有用的。有些场景下,某些字段我们并不想要使用Java提供给我们的序列化方式,而是想要以自定义的方式去序列化它,比如ArrayList的elementData、HashMap的table(至于为什么在之后写这两个类的时候会解释原因),就可以通过将这些字段声明为transient,然后在writeObject和readObject中去使用自己想要的方式去序列化它们。上面的例子SerializObject这个类修改一下,主函数不需要修改
1 public class SerializableObject implements Serializable
private static final long serialVersionUID = 1L;
private String str0;
private transient String str1;
private static String str2 = "abc";
public SerializableObject(String str0, String str1)
this.str0 = str0;
this.str1 = str1;
public String getStr0()
return str0;
public String getStr1()
return str1;
private void writeObject(java.io.ObjectOutputStream s) throws Exception
System.out.println("我想自己控制序列化的过程");
s.defaultWriteObject();
s.writeInt(str1.length());
for (int i = 0; i & str1.length(); i++)
s.writeChar(str1.charAt(i));
private void readObject(java.io.ObjectInputStream s) throws Exception
System.out.println("我想自己控制反序列化的过程");
s.defaultReadObject();
int length = s.readInt();
char[] cs = new char[length];
for (int i = 0; i & i++)
cs[i] = s.readChar();
str1 = new String(cs, 0, length);
直接看一下运行结果
我想自己控制序列化的过程
我想自己控制反序列化的过程
str0 = str0
str1 = str1
看到,程序走到了我们自己写的writeObject和readObject中,而且被transient修饰的str1也成功序列化、反序列化出来了----因为手动将str1写入了文件和从文件中读了出来。不妨再看一下s.txt文件的二进制:
看到橘黄色的部分就是writeObject方法追加的str1的内容。至此,总结一下writeObject和readObject的通常用法:
先通过defaultWriteObject和defaultReadObject方法序列化、反序列化,然后在文件结尾追加需要额外序列化的内容/从文件的结尾读取额外需要读取的内容。&标签:
&&国之画&&&& &&&&chrome插件
版权所有 京ICP备号-2
迷上了代码!平时我们在Java内存中的对象,是无 法进行IO操作或者网络通信的,因为在进行IO操作或者网络通信的时候,人家根本不知道内存中的对象是个什么东西,因此必须将对象以某种方式表示出来,即 存储对象中的状态。一个Java对象的表示有各种各样的方式,Java本身也提供给了用户一种表示对象的方式,那就是序列化。换句话说,序列化只是表示对 象的一种方式而已。OK,有了序列化,那么必然有反序列化,我们先看一下序列化、反序列化是什么意思。
序列化:将一个对象转换成一串二进制表示的字节数组,通过保存或转移这些字节数据来达到持久化的目的。
反序列化:将字节数组重新构造成对象。
默认序列化
序列化只需要实现java.io.Serializable接口就可以了。序列化的时候有一个serialVersionUID参数,Java序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。 在进行反序列化,Java虚拟机会把传过来的字节流中的serialVersionUID和本地相应实体类的serialVersionUID进行比较, 如果相同就认为是一致的实体类,可以进行反序列化,否则Java虚拟机会拒绝对这个实体类进行反序列化并抛出异常。serialVersionUID有两 种生成方式:
1、默认的1L
2、根据类名、接口名、成员方法以及属性等来生成一个64位的Hash字段
如果实现 java.io.Serializable接口的实体类没有显式定义一个名为serialVersionUID、类型为long的变量时,Java序列化 机制会根据编译的.class文件自动生成一个serialVersionUID,如果.class文件没有变化,那么就算编译再多 次,serialVersionUID也不会变化。换言之,Java为用户定义了默认的序列化、反序列化方法,其实就是ObjectOutputStream的defaultWriteObject方法和ObjectInputStream的defaultReadObject方法。看一个例子:
1 public class SerializableObject implements Serializable
private static final long serialVersionUID = 1L;
private String str0;
private transient String str1;
private static String str2 = "abc";
public SerializableObject(String str0, String str1)
this.str0 = str0;
this.str1 = str1;
public String getStr0()
return str0;
public String getStr1()
return str1;
1 public static void main(String[] args) throws Exception
File file = new File("D:" + File.separator + "s.txt");
OutputStream os = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(new SerializableObject("str0", "str1"));
oos.close();
InputStream is = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(is);
SerializableObject so = (SerializableObject)ois.readObject();
System.out.println("str0 = " + so.getStr0());
System.out.println("str1 = " + so.getStr1());
ois.close();
先不运行,用一个二进制查看器查看一下s.txt这个文件,并详细解释一下每一部分的内容。
第1部分是序列化文件头
◇AC ED:STREAM_MAGIC序列化协议
◇00 05:STREAM_VERSION序列化协议版本
◇73:TC_OBJECT声明这是一个新的对象
第2部分是要序列化的类的描述,在这里是SerializableObject类
◇72:TC_CLASSDESC声明这里开始一个新的class
◇00 1F:十进制的31,表示class名字的长度是31个字节
◇63 6F 6D ... 65 63 74:表示的是&com.xrq.test.SerializableObject&这一串字符,可以数一下确实是31个字节
◇00 00 00 00 00 00 00 01:SerialVersion,序列化ID,1
◇02:标记号,声明该对象支持序列化
◇00 01:该类所包含的域的个数为1个
第3部分是对象中各个属性项的描述
◇4C:字符"L",表示该属性是一个对象类型而不是一个基本类型
◇00 04:十进制的4,表示属性名的长度
◇73 74 72 30:字符串&str0&,属性名
◇74:TC_STRING,代表一个new String,用String来引用对象
第4部分是该对象父类的信息,如果没有父类就没有这部分。有父类和第2部分差不多
◇00 12:十进制的18,表示父类的长度
◇4C 6A 61 ... 6E 67 3B:&L/java/lang/S&表示的是父类属性
◇78:TC_ENDBLOCKDATA,对象块结束的标志
◇70:TC_NULL,说明没有其他超类的标志
第5部分输出对象的属性项的实际值,如果属性项是一个对象,这里还将序列化这个对象,规则和第2部分一样
◇00 04:十进制的4,属性的长度
◇73 74 72 30:字符串&str0&,str0的属性值
从以上对于序列化后的二进制文件的解析,我们可以得出以下几个关键的结论:
1、序列化之后保存的是类的信息
2、被声明为transient的属性不会被序列化,这就是transient关键字的作用
3、被声明为static的属性不会被序列化,这个问题可以这么理解,序列化保存的是对象的状态,但是static修饰的变量是属于类的而不是属于变量的,因此序列化的时候不会序列化它
接下来运行一下上面的代码看一下
str0 = str0
str1 = null
因为str1是一个transient类型的变量,没有被序列化,因此反序列化出来也是没有任何内容的,显示的null,符合我们的结论。
手动指定序列化过程
Java并不强求用户非要使用默认的序列化方式,用户也可以按照自己的喜好自己指定自己想要的序列化方式----只要你自己能保证序列化前后能得到想要的数据就好了。手动指定序列化方式的规则是:
进 行序列化、反序列化时,虚拟机会首先试图调用对象里的writeObject和readObject方法,进行用户自定义的序列化和反序列化。如果没有这 样的方法,那么默认调用的是ObjectOutputStream的defaultWriteObject以及ObjectInputStream的 defaultReadObject方法。换言之,利用自定义的writeObject方法和readObject方法,用户可以自己控制序列化和反序列 化的过程。
这是非常有用的。比如:
1、有些 场景下,某些字段我们并不想要使用Java提供给我们的序列化方式,而是想要以自定义的方式去序列化它,比如ArrayList的 elementData、HashMap的table(至于为什么在之后写这两个类的时候会解释原因),就可以通过将这些字段声明为transient, 然后在writeObject和readObject中去使用自己想要的方式去序列化它们
2、因为 序列化并不安全,因此有些场景下我们需要对一些敏感字段进行加密再序列化,然后再反序列化的时候按照同样的方式进行解密,就在一定程度上保证了安全性了。 要这么做,就必须自己写writeObject和readObject,writeObject方法在序列化前对字段加密,readObject方法在序 列化之后对字段解密
上面的例子SerializObject这个类修改一下,主函数不需要修改:
1 public class SerializableObject implements Serializable
private static final long serialVersionUID = 1L;
private String str0;
private transient String str1;
private static String str2 = "abc";
public SerializableObject(String str0, String str1)
this.str0 = str0;
this.str1 = str1;
public String getStr0()
return str0;
public String getStr1()
return str1;
private void writeObject(java.io.ObjectOutputStream s) throws Exception
System.out.println("我想自己控制序列化的过程");
s.defaultWriteObject();
s.writeInt(str1.length());
for (int i = 0; i & str1.length(); i++)
s.writeChar(str1.charAt(i));
private void readObject(java.io.ObjectInputStream s) throws Exception
System.out.println("我想自己控制反序列化的过程");
s.defaultReadObject();
int length = s.readInt();
char[] cs = new char[length];
for (int i = 0; i & i++)
cs[i] = s.readChar();
str1 = new String(cs, 0, length);
直接看一下运行结果
我想自己控制序列化的过程
我想自己控制反序列化的过程
str0 = str0
str1 = str1
看到,程序走到了我们自己写的writeObject和readObject中,而且被transient修饰的str1也成功序列化、反序列化出来了----因为手动将str1写入了文件和从文件中读了出来。不妨再看一下s.txt文件的二进制:
看到橘黄色的部分就是writeObject方法追加的str1的内容。至此,总结一下writeObject和readObject的通常用法:
先通过defaultWriteObject和defaultReadObject方法序列化、反序列化对象,然后在文件结尾追加需要额外序列化的内容/从文件的结尾读取额外需要读取的内容。&
复杂序列化情况总结
虽然Java的序列化能够保证对象状态的持久保存,但是遇到一些对象结构复杂的情况还是比较难处理的,最后对一些复杂的对象情况作一个总结:
1、当父类继承Serializable接口时,所有子类都可以被序列化
2、子类实现了Serializable接口,父类没有,父类中的属性不能序列化(不报错,数据丢失),但是在子类中属性仍能正确序列化
3、如果序列化的属性是对象,则这个对象也必须实现Serializable接口,否则会报错
4、反序列化时,如果对象的属性有修改或删减,则修改的部分属性会丢失,但不会报错
5、反序列化时,如果serialVersionUID被修改,则反序列化时会失败
阅读(...) 评论()}

我要回帖

更多关于 实体类实现序列化接口 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信