您的位置:首页 > 编程语言 > Java开发

我对JAVA序列化和反序列化的理解

2014-07-31 16:49 459 查看
今天一个朋友问我,JAVA序列化到底是怎么回事儿,是有什么作用,一时没答上来,通过查阅了各种文档,还有jdk才算是弄明白了,下面我用简单明了的语言和程序来解释一下:

JAVA语言只能将实现了Serializable接口的类的对象保存到文件和网络传输流中,这句话很重要一定要理解;

序列化是为反序列化服务的,序列化是让对象转化成字节流能通过网络传输或者保存到文件中,反序列化就是从网络传输流或者文件中能反编译出这个对象,主要应用例如:RMI要利用对象序列化运行远程主机上的服务,就像在本地机上运行对象时一样。

下面用实际的例子来解释一下:

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.Serializable;

public class Test implements Serializable{

// private static final long serialVersionUID = 7675836097652452739L;

private String name;

private int age;

public static void main(String[] args) {
Test t = new Test();
t.setAge(26);
t.setName("皇上");
Test.writeObjectToFile(t);
System.out.println(Test.readObjectFromFile());
}

public static void writeObjectToFile(Object obj){

        File file =new File("e://test.txt");

        FileOutputStream out = null;

        try {

            out = new FileOutputStream(file);

            ObjectOutputStream oos=new ObjectOutputStream(out);

            oos.writeObject(obj);

            oos.flush();

            oos.close();

            System.out.println("写入对象成功");

        } catch (IOException e) {

            System.out.println("写入对象失败");

            e.printStackTrace();

        }

    }

public static Object readObjectFromFile(){

        Object temp=null;

        File file =new File("e://test.txt");

        FileInputStream in = null;

        try {

            in = new FileInputStream(file);

            ObjectInputStream ois=new ObjectInputStream(in);

            temp=ois.readObject();

            ois.close();

            System.out.println("读取对象成功");

        } catch (IOException e) {

            System.out.println("读取对象失败");

            e.printStackTrace();

        } catch (ClassNotFoundException e) {

            e.printStackTrace();

        }

        return temp;

    }

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "Test [name=" + name + ", age=" + age + "]";
}

}

执行main方法,打印结果如下:

写入对象成功

读取对象成功

Test [name=皇上, age=26]

发现读写都成功,说明我们序列化和反序列化成功。

现在去掉Serializable实现,再执行main方法,打印结果如下:

写入对象失败

java.io.NotSerializableException: Test
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1183)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:347)
at Test.writeObjectToFile(Test.java:30)
at Test.main(Test.java:20)

读取对象失败

java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: Test
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1354)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
at Test.readObjectFromFile(Test.java:47)
at Test.main(Test.java:21)

Caused by: java.io.NotSerializableException: Test
at java.i
4000
o.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1183)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:347)
at Test.writeObjectToFile(Test.java:30)
at Test.main(Test.java:20)

null

发现读写都不成功了,没有序列化的对象既不能写入文件,同时也不能被读出来;

这也证明了上面说的一个话:JAVA语言只能将实现了Serializable接口的类的对象保存到文件中。

然后大家可能会发现我注释了private static final long serialVersionUID = 7675836097652452739L;

这行代码是做什么用的呢?

序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类(是类不是对象哦)相关联,该序列号在反序列化过程中用于验证序列化对象的发送方和接收方是否为该对象加载了与序列化兼容的类。如果接收者加载的该对象的类的
serialVersionUID 与对应的发送者的类的版本号不同,则反序列化将会导致 InvalidClassException。序列化类可以通过声明名为 "serialVersionUID" 的字段(该字段必须是静态 (static)、最终 (final) 的 long 型字段)显式声明其自己的 serialVersionUID

下面验证一下,我们打开serialVersionUID注释,同时Test还需要实现Serializable接口,

把main方法中的Test.writeObjectToFile(t);这行注释掉,我们只从文件中读数据

执行main方法,打印结果如下:

读取对象失败

java.io.InvalidClassException: Test; local class incompatible: stream classdesc serialVersionUID = 3332569308856534758, local class serialVersionUID = 7675836097652452739
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:617)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1622)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1517)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1771)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
at Test.readObjectFromFile(Test.java:47)
at Test.main(Test.java:21)

null

为什么会打开serialVersionUID这行代码就会报错呢,这不是在同一个类里执行的程序吗?

是的,是在一个类里执行的,但是写进文件的对象是没有serialVersionUID静态常量的(使用的是默认的),而读的时候我增加了serialVersionUID
静态常量

 这样我们简单的验证了 serialVersionUID
与对应的发送者的类的版本号不同,则反序列化将会导致 InvalidClassException。

如有其他意见请回复,期待和各位共同讨论~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息