Serializable接口的意义和用法总结
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Serializable接⼝的意义和⽤法总结
Serializable是Java提供的序列化接⼝,是⼀个空接⼝,为对象提供标准的序列化与反序列化操作。
使⽤Serializable实现序列化过程相当简单,只需要在类声明的时候指定⼀个
标识,便可以⾃动的实现默认的序列化过程。
private static final long serialVersionUID = 1L;
上⾯已经说明让对象实现序列化,只需要让当前类实现Serializable接⼝,并且声明⼀个serialVersionUID就可以了,⾮常的简单⽅便。
实际上serialVersionUID都不是必须的,
没有它同样可以正常的实现序列化操作。
User类就是⼀个实现了Serialzable的类,它是可以被序列化和反序列化的。
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String userId;
private String userName;
}
通过Serializable实现对象的序列化过程⾮常的简单,⽆需任何操作,系统就为我们⾃动实现了。
如何进⾏对象的序列化与反序列化操作也是⾮常的简单,只需要通过
ObjectOutputStream,ObjectInputStream进⾏操作就可以了。
//序列化过程
public void toSerial() {
try {
User user = new User("id", "user");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.txt"));
objectOutputStream.writeObject(user);
objectOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//反序列化过程
public void fromSerial(){
try {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("user.txt"));
User user = (User) objectInputStream.readObject();
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
是的,你没有看错,序列化与反序列化操作过程就是这么的简单。
只需要将User写⼊到⽂件中,然后再从⽂件中进⾏恢复,恢复后得到的内容与之前完全⼀样,但是两者是不同
的对象。
当然了前⾯提到过⼀个问题,如果将serialVersionUID去掉会产⽣什么样的影响呢?
刚开始提到了,serialVersionUID要不要指定呢?如果不指定会出现什么样的后果?如果指定了以后后边的值⼜代表着什么意思呢?同时我们也要明⽩,既然系统指定了这个字段,那么肯定是有它的作⽤的。
这个serialVersionUID是⽤来辅助对象的序列化与反序列化的,原则上序列化后的数据当中的serialVersionUID与当前类当中的serialVersionUID⼀致,那么该对象才能被反序列化成功。
这个serialVersionUID的详细的⼯作机制是:在序列化的时候系统
我这个操作的流程是:⾸先在⼿机sd卡的根⽬录创建⼀个⽂件user.txt,在创建的时候做出判断,如果user.txt不存在,则进⾏创建⽂件操作并进⾏序列化写⼊操作。
如果user.txt存
在那么我们就直接读取并进⾏反序列化操作。
我在创建完成后,在User⾥⾯多加了⼀个属性,然后在进⾏反序列化操作的时候就报出了这样的错误,当然前提是我把类中声明的
serialVersionUID去掉了。
当我把serialVersionUID加上的时候,我在创建完user.txt后我在进⾏序列化读取,成功的拿到了之前存进去的数据。
这样的⼀个操作过程就让我们很清楚的看出来了
serialVersionUID在序列化与反序列化过程当中的作⽤。
这个字段的作⽤显⽽易见,加⼊我们升级了系统,在User当中增加了字段属性,那么我们在⼿动添加了serialVersionUID之后,我们在进⾏序列化和反序列化的时候就会很从
容,就不会再出现crash的情况了。
另外强调两点,静态成员变量属于类不属于对象所以不参与序列化的过程,还有如果使⽤transient标记的成员变量不参与序列化过程。
总结(重点思考)
⼯作中我们经常在进⾏持久化操作和返回数据时都会使⽤到javabean来统⼀封装参数,⽅便操作,⼀般我们也都会实现Serializable接⼝,那么问题来了,⾸先:为什么要进⾏序
列化;其次:每个实体bean都必须实现serializabel接⼝吗?最后:我做⼀些项⽬的时候,没有实现序列化,同样没什么影响,到底什么时候应该进⾏序列化操作呢?
⽹上找了很多资料,但是感觉⼤都没有说的很清楚,所以结合⾃⼰的理解做⼀下总结。
⾸先第⼀个问题,实现序列化的两个原因:1、将对象的状态保存在存储媒体中以便可以在以后重新创建出完全相同的副本;2、按值将对象从⼀个应⽤程序域发送⾄另⼀个
应⽤程序域。
实现serializabel接⼝的作⽤是就是可以把对象存到字节流,然后可以恢复,所以你想如果你的对象没实现序列化怎么才能进⾏持久化和⽹络传输呢,要持久化和⽹
络传输就得转为字节流,所以在分布式应⽤中及设计数据持久化的场景中,你就得实现序列化。
第⼆个问题,是不是每个实体bean都要实现序列化,答案其实还要回归到第⼀个问题,那就是你的bean是否需要持久化存储媒体中以及是否需要传输给另⼀个应⽤,没有的
话就不需要,例如我们利⽤fastjson将实体类转化成json字符串时,并不涉及到转化为字节流,所以其实跟序列化没有关系。
第三个问题,有的时候并没有实现序列化,依然可以持久化到数据库。
这个其实我们可以看看实体类中常⽤的数据类型,例如Date、String等等,它们已经实现了序列化,
⽽⼀些基本类型,数据库⾥⾯有与之对应的数据结构,从我们的类声明来看,我们没有实现serializabel接⼝,其实是在声明的各个不同变量的时候,由具体的数据类型帮助我们
实现了序列化操作。
另外需要注意的是,在NoSql数据库中,并没有与我们java基本类型对应的数据结构,所以在往nosql数据库中存储时,我们就必须将对象进⾏序列化,同时在⽹络传输中我
们要注意到两个应⽤中javabean的serialVersionUID要保持⼀致,不然就不能正常的进⾏反序列化。
下⾯是看到的别⼈整理的⼀个业务场景,感觉描述的挺不错;整理到这⾥,供⼤家借鉴理解
今天在使⽤eclipse开发的时候,遇到⼀个warning,看到warning我总觉得不爽,使⽤⾃动修复后,发现eclipse在代码中加⼊了“private static final long serialVersionUID = 1L;”。
其实之前就遇过这种情况了,只是没有去了解,于是今天我就查了⼀下serialVersionUID 这个变量的信息。
原来serialVersionUID是可序列化类的⼀个版本标识,在反序列化的时候使⽤这个标识的值来判断序列化和反序列化的对象类型是否⼀致。
Java的序列化机制是通过在运⾏时判
断类的serialVersionUID来验证版本⼀致性的。
在进⾏反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进⾏⽐较,如果相同
就认为是⼀致的,可以进⾏反序列化,否则就会出现序列化版本不⼀致的异常(InvalidClassException)。
当你⼀个类实现了Serializable接⼝,如果没有定义
serialVersionUID,Eclipse会提供这个提⽰功能告诉你去定义之。
Eclipse中有两种⽣成⽅式:
⼀个是默认的1L,⽐如:“private static final long serialVersionUID = 1L;";
⼀个是根据类名、接⼝名、成员⽅法及属性等来⽣成⼀个64位的哈希字段,⽐如:“private static final long serialVersionUID = -8940196742313994740L;”。
使⽤serialVersionUID要注意以下⼏点:
1.当实现java.io.Serializable接⼝的实体(类)没有显式地定义⼀个名为serialVersionUID,类型为long的变量时,Java序列化机制会根据编译的class⾃动⽣成⼀个
serialVersionUID作序列化版本⽐较⽤,这种情况下,只有同⼀次编译⽣成的class才会⽣成相同的serialVersionUID 。
如果我们不希望通过编译来强制划分软件版本,即实现序
列化接⼝的实体能够兼容先前版本中未作更改的类,就需要显式地定义⼀个名为serialVersionUID,类型为long的变量,不修改这个变量值的序列化实体都可以相互进⾏串⾏化
和反串⾏化。
2.记住应该总是在可序列化的类中包含这个字段,即使是在第⼀个类版本中,以便提醒⾃⼰这个字段的重要性。
不要在未来的版本中改变这个字段值,除⾮你有意要改变类使其与旧的序列化对象不兼容。
3.如果你的类序列化到硬盘上⾯后,你更改了类别的field(增加或减少或改名),当你反序列化时,就会出现异常的,这样就会造成不兼容性的问题。
但当serialVersionUID相同时,它就会将不⼀样的field以type的预设值Deserialize,这个可以避开不兼容性的问题。
4.当我们的系统不太经常需要序列化类时,可以去掉这些警告,做如下设置:Window-->Preferences-->Java,将serializable class without serialVersionUID的设置由warning 改为Ignore。
然后Eclipse会重新编译程序,那些警告信息也就消失了。
但如果在开发⼤量需要序列化的类的时候,建议还原为原来的设置。
这样可以保证系统的性能和健壮。
参考⽂章:。