Java的序列机制:
序列化 | 把JVM里的对象转换为字节数据,此数据可传输给其它应用程序或保存到固定存储(以IO流的方式) |
反序列化 | 把对象序列化的字节数据转换为JVM里的对象 |
类或其继承的父类只要实现java.io.Serializable接口,其对象即可被序列化。此接口为标记接口,不包含任何方法。如果没有实现此接口,在序列化时会触发NotSerializableException
使用ObjectInput、ObjectOutput可以序列化对象,典型代码如下,需要注意的是输入/输出方法要互相对应:
//序列化对象 private void writeObject(String filePath,Object o){ try{ ObjectOutput out=new ObjectOutputStream(new FileOutputStream(new File(filePath))); out.writeObject(o);//需要与输入流的输入方法对应 out.flush(); out.close(); }catch(Exception e){ e.printStackTrace(); } } //反序列化对象 private Object readObject(String filePath){ try{ ObjectInput input=new ObjectInputStream(new FileInputStream(new File(filePath))); Object object=input.readObject(); //需要与输出流的输出方法对应 input.close(); return object; }catch(Exception e){ e.printStackTrace(); return null; } }
与序列化相关的方法和属性包括:
private static final long serialVersionUID = 1L; | 序列号,用于验证序列化类的版本号 |
private void writeObject(ObjectOutputStream out) throws IOException | 如果需要自定义序列化数据(即写入序列化输出流的数据),应覆盖此方法 |
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException | 如果需要自定义反序列化数据(即从序列化输入流读取的数据)或者需要在反序列化时自行设置对象属性值,应覆盖此方法 |
private void readObjectNoData() throws ObjectStreamException | 如果覆盖了此方法,对象的属性和输入流所包含的属性不匹配,会回调此方法,详见后面解释 |
private Object writeReplace() throws ObjectStreamException | 如果需要使用另一个对象代替当前被序列化的对象,应该覆盖此方法 |
private Object readResolve() throws ObjectStreamException | 如果需要从另一个对象反序列化为当前对象,应该覆盖此方法 |
默认情况下,只有非static和非transient的属性可以被序列化(因为是对象序列化,所以默认static属性不会被序列化)。在writeObject()、readObject()可以调用ObjectOutputStream.defaultWriteObject()、ObjectInputStream.defaultReadObject()实现默认的序列化。可以在writeObject()/readObject()对序列化属性进行加密/解密。
如果同一个对象被多次写入序列化输出流里,那么仅有第一次对象序列化的字节会被写入,后续写入的是对第一次写入字节的引用数据。
为了方便解释(代码可能后期补上),我们假设客户端和服务端都具有com.bingo.Hello类,两个类的数据结构完全相同。为了区分用c-Hello表示客户端的Hello类,s-Hello表示服务端的Hello类。假设服务端把s-Hello对象序列化后的字节数据发送给客户端进行反序列化。
如果s-Hello的serialVersionUID=1L, c-Hello的serialVersionUID=2L,那么客户端在反序列化时会抛出InvalidClassException,因此两者的序列号不匹配。如果没有显式的声明serialVersionUID,那么JVM会自动生成一个不重复的序列号。如果客户端和服务端处于不同的JVM实例,那么对同一个com.bingo.Hello类,生成的序列号是不同的,这可能会导致反序列化时失败。因此建议总是显式的声明serialVersionUID。
如果需要客户端和服务端使用同一个Hello类,那么服务端的s-Hello应该显式声明使用一个随机的serialVersionUID,这样客户端只能从服务端加载s-Hello.class,然后再进行反序列化。如果服务端修改了s-Hello,那么应同时修改serialVersionUID,以便客户端加载最新的s-Hello.class(即实现版本管理功能)。如果不需要使用同一个Hello类,那么建议显式的声明serialVersionUID=1L
假设c-Hello实现了ParentHello类(仅存在于客户端),而s-Hello没有实现此类。那么在反序列化时会调用
readObjectNoData()(个人认为是调用s-Hello的readObjectNoData(),待测试),如果不希望反序列化时改变对象的继承结构,那么应在readObjectNoData()里直接抛出InvalidObjectException
writeReplace()/readResolve()可以使用另一个对象代替当前对象进行序列化/反序列化,一般如果单例类可以序列化,那么需要覆盖单例类的readResolve()返回当前单例对象,避免使用反序列化的创建的另一个单例对象。此方法的另一种用法是实现flyweight设计模式,考虑以下类:
public class State implements Serializable { public static final State ON=new State(0); public static final State OFF=new State(1); private int value; private State(int value){ this.value=value; } }
如果在代码里大量使用了State.ON、State.OFF,那么在反序列化时会创建大量的State对象(默认情况下反序列化将创建一个新的对象),可以使用如下代码避免此问题:
public class State implements Serializable { public static final State ON=new State(0); public static final State OFF=new State(1); private int value; private State(int value){ this.value=value; } private Object writeReplace() throws ObjectStreamException{ return value==ON.value?ON:OFF;//序列化同一个对象 } private Object readResolve() throws ObjectStreamException{ return value==ON.value?ON:OFF;//反序列化同一个对象 } }
另一种方法是使用序列化代理类(类似flyweight设计模式),如下:
public class State implements Serializable { public static final State ON=new State(0); public static final State OFF=new State(1); private int value; private State(int value){ this.value=value; } //使用StateProxy作为序列化对象。State不需要覆盖readResolve() private Object writeReplace() throws ObjectStreamException{ return value==ON.value?StateProxy.ON:StateProxy.OFF; } //序列化代理类 private static final class StateProxy implements Serializable{ final static StateProxy ON=new StateProxy(State.ON.value); final static StateProxy OFF=new StateProxy(State.OFF.value); private int value; private StateProxy(int value){ this.value=value; } //返回反序列的被代理的State对象 private Object readResolve() throws ObjectStreamException{ return value==ON.value?State.ON:State.OFF; } } }
以下为测试代码:
@Test public void testStateSerial(){ writeObject("./state.ser", State.OFF); State state=(State)readObject("./state.ser"); Assert.assertSame(state, State.OFF); }
注:如果序列化类是类似State的简单的类,最好的方式是使用枚举类。同一个枚举量反序列化后返回同一个对象。所以也可以使用枚举类实现单例模式
如果子类实现了Serializable接口,但是父类没有实现。那么JVM不会序列化父类对象,但是序列化子类对象时必须调用父类的构造函数,所以此情况父类必须提供无参的构造函数。
Apache Commons提供了序列化工具类 ,具体请参考官方帮助文档
其它需要注意的地方:
1.修改可序列化类的结构,可能导致已经序列化的对象在反序列化时失败(可重新加载最新的class文件后再进行反序列化 )
2.反序列化可能引入安全问题
3.序列化增加测试工作(原因见第1条)
4.建议仅有值对象的类(如Date,BigInteger)提供序列化功能
5.接口和用于继承的父类,内部类应尽量不要实现Serializable接口
相关推荐
驱动更新软件
与传统矩形天线相比,在保持方向性、最大增益等参数性能基本不变的条件下,基于超材料结构的天线辐射贴片尺寸分别为12.44mm×9.12 mm和11.74 mm×9.1 mm,相比传统矩形天线分别缩小了35.2%和41.82%,辐射贴片小型化效果...
该研究论文涉及由马杜赖的马杜赖·卡马拉杰大学(Madurai Kamaraj University)的教职员工使用ICT进行信息创建,即类型,频率,持续... 31%(11.74%)和30%(11.36%)的受访者使用ICT工具和设备2到3个小时以上。
文件格式:dwg。文件大小:1.78MB。文档类型:施工图,建筑...单栋总开间x进深:14.72x11.74米 户型:两室一厅一卫 资料包含:各层平面图、立面图、剖面图、门窗表、门窗大样、楼梯大样、节点详图、户型放大平面图 。
结果表明,工作面初次来压步距44 m,回采初期周期来压步距11.74 m,回采中期周期来压步距增大至14.1 m,工作面周期来压对支架作用强度整体显示出强、弱交替变化规律,周期来压强度呈现"两端小,中间大"的特征。工作面周期...
版本名称 AP5000 V200R008C10SPCg00 软件名称 AP5010DN-AGN_V200R008C10SPCg00.zip 发布时间 2019-11-11 文件大小 11.74MB
版本名称 AP5000 V200R008C10SPCg00 软件名称 AP5010SN-GN_V200R008C10SPCg00.zip 发布时间 2019-11-11 文件大小 11.74MB
mod_qos是Apache Web Server的服务质量模块。 它实现了可以为不同请求提供不同优先级的控制机制,并根据可用资源控制服务器访问。
热暴露400 h后,抗拉强度(σb)提高了30 MPa,断面收缩率(ψ)降低了11.74%。热暴露过程中,合金塑性的降低主要是硅化物和α2相协同作用的结果,其中α2相对力学性能的影响起主导作用;α2相的稳定化和长大过程是...
采用微波辅助水蒸气蒸馏法提取艾叶精油,在单因素实验基础上,通过响应面法优化艾叶精油提取的工艺条件,气相色谱-质谱法对精油的化学成分进行分析,并应用峰面积归一化法测定成分的相对百分含量 结果表明:最佳工艺...
背景:乳腺癌是根据分子亚型定义的。 每种分子亚型定义不同的疾病行为,需要不同的... 中位年龄为51岁,平均年龄为51.64±11.74,范围为24至85岁。 ER阳性患者总数为706例(70.5%),PR阳性患者总数为667例(66.6%
这个项目是一个简单的数字设定圈的实现,它是从我之前的DSC项目发展而来的,但是现在使用便宜的光学编码器(和GT2同步带+齿轮)代替了磁力计。 该DSC实现了以下目的: 对于DIY制造商或Arduino发烧友而言,构建...
于2005年4月至7月研究了厦门海沧吴冠滩涂人工红树林种植区秋茄(Kandelia candel)幼林上藤壶分布的特征・结果表明...其中纹藤壶、白条地藤壶为优势种.(2)当高程大于-11.74 cm时,藤壶的空间分布格局总体上为聚集分布.
选取体重约15 kg的乌金猪54头,随机分为3组,分别饲喂消化能为14.22,12.89,11.74 MJ/kg的日粮,在30,60,100 kg体重时屠宰,分别测定血液中胰岛素和葡萄糖含量及胴体脂肪沉积率,取皮下脂肪组织用荧光定量 ...
经调查和统计分析,中国污水处理厂78.48%的COD是经过好氧为主处理工艺去除, 9.78%是经过非生物过程去除,仅11.74%的COD经过完全厌氧工艺处理。因而,针对不同处理类型建立适合中国的排放因子非常重要。根据现场实测和...
采用样方法研究了首曲湿地功能区“黑土滩”...封育后补播“高寒1号”生态草种相对于封育前,使得退化草甸的盖度增加了56.00%,高度增加了11.74 cm,地上生物量增加了222.24 g/m2,可食牧草比例增加了55.98%,物种数由5种/
为研究日粮不同能量水平对乌金猪脂肪代谢相关酶活性的影响,选取体重约15 kg的乌金猪54头,随机分为3组,下设3个重复,每个重复6头,分别饲喂消化能为14.22 (高能组)、12.89 (中能组)和11.74 MJ /kg (低能组)的日粮,在...
为研究日粮不同能量和蛋白质水平对...高消化能(HE,14.22 MJ /kg)、中消化能(ME,12.89 MJ /kg) 和低消化能(LE,11.74 MJ /kg),蛋白水平为:高蛋白质(HP,18%,16%,14%)、中蛋白质(MP,16%,14%,12%)和低蛋白质(LP,14%,12%和
利用实验生态学的方法研究不同温度(15、20、25、30和35℃)、盐度(0、5、10、15和20)、pH( 5、6、7、8和9)和体质量(1.93±0.08、3.43±0.12、7.95±0.34、11.74±0.41和15.84±0.61 g)对尼罗罗非鱼幼鱼窒息点的影响,...
51种柳树优良无性系中柳Q43的最大净光合速率最高,为32.50μmol/(m2・s),绵毛柳的最大净光合速率最低,仅为11.74μmol/(m2・s);柳2462的暗呼吸速率较小,仅为0.43μmol/(m2・s),垂柳109的