转载出处:http://blog.csdn.net/mazhimazh/article/details/20292331
使用Java的序列化和反序列化可以实现信息的持久存储、或者也可以实现Java对象的深克隆。在前面文章讲解过使用序列化和反序列化来实现对象克隆,如下:
Java之基础 - 深克隆与浅克隆(参见文章:http://blog.csdn.net/mazhimazh/article/details/16828505)
下面来具体讲解一下序列化。能够进行序列化的类必须要实现Serializable接口,这个接口的定义如下:
- public interface Serializable { }
这个接口仅用作一个标识,表示这个类可以进行序列化和反序列化。典型代码如下:
- class Shitt implements Serializable{ // 必须实现Serializable接口 }
将如下的类进行序列化和反序列化时继承序列化接口:
- class Shitt implements Serializable{
- private static final long serialVersionUID = 1L;
- private String name;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
进行类的序列化和反序列化操作:
- public static void save(Shitt t) { // 序列化给定的类
- try {
- FileOutputStream fs = new FileOutputStream("test.txt");
- ObjectOutputStream os = new ObjectOutputStream(fs);
- os.writeObject(t);
- os.flush();
- os.close();
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- }
- public static void read() { //反序列化给定的类
- try {
- FileInputStream fs = new FileInputStream("test.txt");
- ObjectInputStream ois = new ObjectInputStream(fs);
- Shitt login = (Shitt) ois.readObject();
- System.out.println(login.getName());
- ois.close();
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- }
需要注意的几个几点:
(1)在每个要序列化的类中加入private static final 的serialVersionUID,这样,即使在某个对象被序列化之后,它所对应的类被修改了,该对象也依然可以被正确地反序列化。举个例子:
- public class MeTest{
- public static void main(String[] args) {
- Shitt t = new Shitt();
- t.setName("mazhi");
- MeTest.save(t);
- MeTest.read();
- }
- // 省略save()和read()方法
- }
最终运行的结果如下:
mazhi
现在修改序列化类为:
- class Shitt implements Serializable{
- private static final long serialVersionUID = 1L;
- private String name;
- private int p;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public int getP() {
- return p;
- }
- public void setP(int p) {
- this.p = p;
- }
- }
现在只调用read()方法进行读取操作,可以看到能够正常读取。去掉serialVersionUID属性或修改后报错:
local class incompatible:
实现序列化接口的实体能够兼容先前版本,不修改这个变量值的序列化实体都可以相互进行串行化和反串行化。
(2)如果一个类中有引用类型的实例变量,这个引用类型也要实现Serializable接口。否则就会抛出异常。或者在不实例Serializable接口时,为这个引用变量加上transient关键字即可。
首先说明一下引用类型的实例变量怎么理解,如下:
- class Father{ } // 没有实现Serializable接口
- class Shitt implements Serializable{
- private String name;
- public Father t;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
但是在进行序列化的时候不使用这个t,也就只是声明而不进行实例同样不会出现错误。如果要进行实例化,就需要实现接口,或者加上transient关键字也行,然后再序列化后就不会出错,如下:
- Shitt t = new Shitt();
- t.setName("mazhi");
- t.t=new Father();
- MeTest.save(t);
- MeTest.read();
一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。
(3)被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。
- class Father{
- public int a;
- public int getA() {
- return a;
- }
- public void setA(int a) {
- this.a = a;
- }
- }
- class Shitt extends Father implements Serializable{
- private String name;
- public static String str="my name is mazhi";
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
在这个类中,到底哪些变量的值能够进行序列化,哪些不能呢?测试如下:
- Shitt t = new Shitt();
- t.setName("mazhi");
- t.a=3;
- MeTest.save(t);
- MeTest.read();
在read()方法中打印str和a的值:
- System.out.println(login.getName()+" "+login.a);
- System.out.println(login.str);
最终运行的结果如下:
mazhi 0
my name is mazhi
可以看出Father类没有实现序列化接口,其定义的属性不能被序列化。最需要注意的是静态变量,这个变量不被序列化,那为什么还可以读出值呢?
因为在类加载时这个变量已经存在于内存中了,而且保持着唯一,不会被垃圾回收。当再运行时,由于静态变量属于类,当然可以取出这个值了。我们可以在另外一个虚拟机上进行测试就不会出现这个问题了。
同时从这个例子还能得出一些结论:
如果一个类实现了Serializable接口,但是它的父类没有实现,那么这个类也可以序列化。需要提醒的是Object超类没有实现这个序列化的接口。为什么呢??因为如果一个类没有实现Serializable接口,但是它的父类实现了,那么这个类也可以序列化。Object实现序列化接口意味着所有的类都可以被序列化,这一概念变得无意义,现时还会造成各种问题。
(4)序列化和反序列化过程中构造函数的调用
- class Father{ // 没有实现Serializable接口
- public Father(){
- System.out.println("父类构造方法");
- }
- }
- class Shitt extends Father implements Serializable{
- public Shitt(){
- System.out.println("子类构造方法");
- }
- }
对Shitt类进行序列化和反序列化后的运行结果如下:
父类构造方法
子类构造方法
父类构造方法
父类加上序列化接口后再次进行序列化和反序列化的操作,结果如下:
父类构造方法
子类构造方法
由上可以得出结论:在进行反序列化过程中还是遵守类的初始化规则,也就是先父类后子类,但是子类的初始化并不是通过调用构造函数等手段,而是从持久化文件中存储的信息进行初始化的。
扩展:
(1)其实可以在类的序列化过程中,指定属性存储的格式,例如XML,反序列化也是一样的,有兴趣的话可以参见其他资料。
(2)既然序列化和反序列化可以实现文件的持久存取,那么与其他一些持久化,例如数据库比起来又怎么样呢?
相关推荐
java 对象的序列化与反序列化 java 对象的序列化与反序列化
java序列化和反序列化java序列化和反序列化java序列化和反序列化
Xson是一个Java对象序列化和反序列化程序。支持Java对象到字节数组的序列化,和从字节数组到Java对象的反序列化。 Maven: <groupId>com.github.xsonorg</groupId> <artifactId>xson-core <version>1.0.1 ...
对象-序列化-反序列化相关资料
Java序列化与反序列化 Java序列化与反序列化 Java序列化与反序列化 Java序列化与反序列化 Java序列化与反序列化
java 的序列化与反序列化举例测试
Java Json序列化与反序列化
java反序列化工具,覆盖jboss、weblogic、websphere。
极好的序列化与发序列化代码。可以处理array集合,数组或者单个对象等的序列化与反序列化。
演示案例:Java 反序列化及命令执行代码测试WebGoat_Javaweb 靶场反序列化测试0x01 注入判断,获取管理员帐号密码:根据提示附件进行 java
java对象 java序列化 java反序列化
在应用java进行c-s开发的时候,尤其涉及到图片和视频之间的传输时,需要用序列化和反序列化技术,希望对您有帮助
但首先要理解的是,“反序列化漏洞”是对一类漏洞的泛指,而不是专指某种反序列化方法导致的漏洞,比如Jackson反序列化漏洞和Java readObject造成的
C++ JSON 序列化与反序列化 相关的博客文章见:http://blog.csdn.net/TragicGuy
什么叫反序列化? 就是再把介质中的东西还原成对象,把石子还原成人的过程。 在进行这些操作的时候都需要这个可以被序列化,要能被序列化,就得给类头加[Serializable]特性。 通常网络程序为了传输安全才这么做。不...
序列化与反序列化 序列化与反序列化 序列化与反序列化 序列化与反序列化 序列化与反序列化
序列化是干什么的? 简单说就是为了保存在内存中的各种对象的状态(也就是实例...虽然你可以用你自己的各种各样的方法来保 存object states,但是Java给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化。
java 常用序列化和反序列化框架使用demo ,java 常用序列化和反序列化框架使用demo
序列化和反序列化的封装类,只要调用就行 序列化和反序列化的封装类 序列化和反序列化的封装类 序列化和反序列化的封装类 序列化和反序列化的封装类