JAVA中对象的序列化

来源:互联网 发布:nginx检查配置文件 编辑:程序博客网 时间:2024/06/06 05:06

对象的序列化

当创建对象时,程序运行时它就会存在,但是程序停止时,对象也就消失了.但是如果希望对象在程序不运行的情况下仍能存在并保存其信息,将会非常有用,对象将被重建并且拥有与程序上次运行时拥有的信息相同。可以使用对象的序列化。 对象的序列化:   将内存中的对象直接写入到文件设备中 对象的反序列化: 将文件设备中持久化的数据转换为内存对象基本的序列化由两个方法产生:一个方法用于序列化对象并将它们写入一个流,另一个方法用于读取流并反序列化对象。    ObjectOutput    writeObject(Object obj)               将对象写入底层存储或流。    ObjectInput    readObject()               读取并返回对象。
由于上述ObjectOutput和ObjectInput是接口,所以需要使用具体实现类。ObjectOutput ObjectOutputStream被写入的对象必须实现一个接口:Serializable否则会抛出:NotSerializableException ObjectInput     ObjectInputStream    该方法抛出异常:ClassNotFountException     ObjectOutputStream和ObjectInputStream 对象分别需要字节输出流和字节输入流对象来构建对象。也就是这两个流对象需要操作已有对象将对象进行本地持久化存储。
案例:序列化和反序列化Cat对象。public class Demo3 {    public static void main(String[] args) throws IOException,            ClassNotFoundException {        Cat cat = new Cat("tom", 3);        FileOutputStream fos = new FileOutputStream(new File("c:\\Cat.txt"));        ObjectOutputStream oos = new ObjectOutputStream(fos);        oos.writeObject(cat);        System.out.println(cat);        oos.close();        // 反序列化        FileInputStream fis = new FileInputStream(new File("c:\\Cat.txt"));        ObjectInputStream ois = new ObjectInputStream(fis);        Object readObject = ois.readObject();        Cat cat2 = (Cat) readObject;        System.out.println(cat2);        fis.close();}class Cat implements Serializable {    public String name;    public int age;    public Cat() {    }    public Cat(String name, int age) {        this.name = name;        this.age = age;    }    @Override    public String toString() {        return "Cat [name=" + name + ", age=" + age + "]";    }}

Serializable

类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。所以需要被序列化的类必须是实现Serializable接口,该接口中没有描述任何的属性和方法,称之为标记接口。    如果对象没有实现接口Serializable,在进行序列化时会抛出NotSerializableException 异常。注意:保存一个对象的真正含义是什么?如果对象的实例变量都是基本数据类型,那么就非常简单。但是如果实例变量是包含对象的引用,会怎么样?保存的会是什么?很显然在Java中保存引用变量的实际值没有任何意义,因为Java引用的值是通过JVM的单一实例的上下文中才有意义。通过序列化后,尝试在JVM的另一个实例中恢复对象,是没有用处的。如下:首先建立一个Dog对象,也建立了一个Collar对象。Dog中包含了一个Collar(项圈)现在想要保存Dog对象,但是Dog中有一个Collar,意味着保存Dog时也应该保存Collar。假如Collar也包含了其他对象的引用,那么会发生什么?意味着保存一个Dog对象需要清楚的知道Dog对象的内部结构。会是一件很麻烦的事情。    Java的序列化机制可以解决该类问题,当序列化一个对象时,Java的序列化机制会    负责保存对象的所有关联的对象(就是对象图),反序列化时,也会恢复所有的相关内容。口的类才可以序列化。如果只是Dog实现了该接口,而Collar没有实现该接口。会发生什么?    Dog类和Collar类
import java.io.Serializable;public class Dog implements Serializable {    private Collar collar;    private String name;    public Dog(Collar collar, String name) {        this.collar = collar;        this.name = name;    }    public Collar getCollar() {        return collar;    }}class Collar {    private int size;    public int getSize() {        return size;    }    public Collar(int size) {        this.size = size;    }}序列化import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectOutputStream;public class Demo4 {    public static void main(String[] args) throws IOException {        Collar coll = new Collar(10);        Dog dog = new Dog(coll, "旺财");        FileOutputStream fis = new FileOutputStream(new File("c:\\dog.txt"));        ObjectOutputStream os = new ObjectOutputStream(fis);        os.writeObject(dog);    }}执行程序,出现了运行时异常。Exception in thread "main" java.io.NotSerializableException: Collar所以我们也必须将Dog中使用的Collar序列化。但是如果我们无法访问Collar的源代码,或者无法使Collar可序列化,如何处理?两种解决方法:一:继承Collar类,使子类可序列化-------------------但是:如果Collar是final类,就无法继承了。并且,如果Collar引用了其他非序列化对象,也无法解决该问题。二:transient-----------此时就可以使用transient修饰符,可以将Dog类中的成员变量标识为transient那么在序列化Dog对象时,序列化就会跳过Collar。public class Demo4 {    public static void main(String[] args) throws IOException,            ClassNotFoundException {        Collar coll = new Collar(10);        Dog dog = new Dog(coll, "旺财");        System.out.println(dog.getCollar().getSize());        FileOutputStream fis = new FileOutputStream(new File("c:\\dog.txt"));        ObjectOutputStream os = new ObjectOutputStream(fis);        os.writeObject(dog);        // 反序列化        FileInputStream fos = new FileInputStream(new File("c:\\dog.txt"));        ObjectInputStream ois = new ObjectInputStream(fos);        Object readObject = ois.readObject();        Dog dog2 = (Dog) readObject;        // Collar未序列化。        dog2.getCollar().getSize();    }}这样我们具有一个序列化的Dog和非序列化的Collar。此时反序列化Dog后,访问Collar,就会出现运行时异常10Exception in thread "main" java.lang.NullPointerException注意:序列化不适用于静态变量,因为静态变量并不属于对象的实例变量的一部分。静态变量随着类的加载而加载,是类变量。由于序列化只适用于对象。基本数据类型可以被序列化public class Demo5 {    public static void main(String[] args) throws IOException {        // 创建序列化流对象        FileOutputStream fis = new FileOutputStream(new File("c:\\basic.txt"));        ObjectOutputStream os = new ObjectOutputStream(fis);        // 序列化基本数据类型        os.writeDouble(3.14);        os.writeBoolean(true);        os.writeInt(100);        os.writeInt(200);        // 关闭流        os.close();        // 反序列化        FileInputStream fos = new FileInputStream(new File("c:\\basic.txt"));        ObjectInputStream ois = new ObjectInputStream(fos);        System.out.println(ois.readDouble());        System.out.println(ois.readBoolean());        System.out.println(ois.readInt());        System.out.println(ois.readInt());        fos.close();    }}

serialVersionUID

用于给类指定一个UID。该UID是通过类中的可序列化成员的数字签名运算出来的一个long型的值。只要是这些成员没有变化,那么该值每次运算都一样。该值用于判断被序列化的对象和类文件是否兼容。如果被序列化的对象需要被不同的类版本所兼容。可以在类中自定义UID。定义方式:static final long serialVersionUID = 42L;
0 0
原创粉丝点击