JAVA中对象的序列化

对象的序列化

当创建对象时,程序运行时它就会存在,但是程序停止时,对象也就消失了.
但是如果希望对象在程序不运行的情况下仍能存在并保存其信息,将会非常有用,对象将被
重建并且拥有与程序上次运行时拥有的信息相同。可以使用对象的序列化。
 对象的序列化:   将内存中的对象直接写入到文件设备中
 对象的反序列化: 将文件设备中持久化的数据转换为内存对象
基本的序列化由两个方法产生:一个方法用于序列化对象并将它们写入一个流,
另一个方法用于读取流并反序列化对象。
    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,就会出现运行时异常
10
Exception 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值