Java教程

JAVA序列化

本文主要是介绍JAVA序列化,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

JAVA9改进的对象序列化

对象序列化的目标是将对象保存到磁盘中,或允许在网络中直接传输对象。对象序列化允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,其他程序一旦获得了这种二进制流,都可以把它恢复成原本的JAVA对象。

对象的序列化指的是讲一个JAVA对象写入IO流中,反序列化指的是从IO流恢复该JAVA对象。

所有可能在网络上传输的对象的类都应该是可序列化的。

为了让某个类可序列化,该类必须事先下面两个接口:

Serializable

Externalizable

其中Serializable是一个标记接口,实现该接口不需要实现任何方法,只是表明这个类可以被序列化。

因为序列化是RMI过程的参数和返回值都必须实现的机制,而RMI又是JAVA EE的基础,因此JAVA EE需要经常用到序列化。

使用对象流实现序列化

使用Serializable实现序列化:

Person p = new Person("张三", 20);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));

oos.writeObject(p);

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
Person p_ = (Person)ois.readObject();

System.out.println(p_.name+p_.age);

其中Person类为:

public class Person implements java.io.Serializable{
    public String name;
    public int age;
    public Person(String n,int a){
        this.name= n;
        this.age = a;
    }

对象引用的序列化

前面的Person类里面的成员变量都是基本类型或String,如果其成员变量是另一个类的类型,那么这个类也应该是被标记为可序列化的。

假如有如下类:

public class Teacher implements java.io.Serializable {
    public String name;
    public Person person;
    public Teacher(String n,Person a){
        this.name= n;
        this.person = a;
    }
}

当有两个Teacher对象,同时引用一个Person对象时:

Person p1 = new Person("张三", 20);
Teacher t1 = new Teacher("zhao",p1);
Teacher t2 = new Teacher("wang",p1);

如果这时先序列化t1,会连带把里面的p1序列化,再序列化t2,也会再序列化p1一次,再序列化p1时,系统又会把p1序列化一次,这时候系统向输出流中写入了三个Person对象,在反序列化时,就会得到3个Person对象,这是t1里面的person和t2里面的person以及p1不是同一个对象,违背了JAVA反序列化的机制的初衷。

JAVA对于这种情况有一种保护措施,每个保存到磁盘中的序列化对象都有一个编号,当程序试图序列化一个对象时,先检查对象是否已被序列化过,如果序列化过,就直接输出一个序列化编号,而不是重新序列化一遍。

JAVA9增加的过滤功能

JAVA9为ObjectInputStream增加了setObjectInputFilter()、getObjectInputFilter()两个方法,第一个方法为对象输入流设置过滤器,当通过ObjectInputStream反序列化对象时,过滤器的checkInput()方法会被自动激发,用于检查序列化数据是否有效。

checkInput()有三种返回值:

Status.REJECTED;拒绝恢复 ALLOWED 允许恢复 UNDECIDED 未决定,程序继续执行检查

自定义序列化

某些情况下我们不希望系统将某些实例的变量值进行序列化,比如账号密码,或者某些变量的类型不能被序列化,我们可以在实例变量前用transient修饰,这时候程序就不会对其序列化。

或者我们可以自定义序列化过程,对于需要特殊序列化处理的类,应该提供如下特殊签名的方法:

private void writeObject(java.io.ObjectOutputStream out)throws IOException

private void readObject(java.io.ObjectInputStream in)throws IOException,ClassNotFounfException

private void readObjectNoData()throws ObjectStreamException

writeObject:负责写入特定类的实例状态,以便相应的readObject()方法可以恢复它。通过重写该方法,程序员可以完全获得对序列化机制的控制,能自主决定对那些实例变量进行序列化,怎样序列化。默认情况下该方法会调用out.defaultWriteObject来保存Java对象的各实例变量,从而可以实现序列化JAVA对象的目的;

readObject:负责从流中读取并且恢复对象实例变量,通过重写该方法,程序员可以完全获得对反序列化机制的控制。默认情况下该方法会调用in.defaultRead-Object来保存Java对象的各实例变量;

readObjectNoData():当序列化流不完整时,该方法可以用来正确的初始化反序列化的对象。例如接收方和发送方的序列反序列化标准不一样,或是拓展的类不同

另一种自定义机制可在序列化时将该对象替换为其他对象,通过如下方法实现该功能:

ANY-ACCESS-MODIFIER Object WriteReplace() throws ObjectStreamException;

该方法会由序列化机制自动调用,

public class Person implements java.io.Serializable{
    public String name;
    public int age;
    public Person(String n,int a){
        this.name= n;
        this.age = a;
    }

    private Object writeReplace() throws ObjectStreamException{
        return new Person("zz",10);
    }
}

或者可以

private Object writeReplace() throws ObjectStreamException{
    List<Object> l = new ArrayList<Object>();
    l.add(name);
    l.add(age);
    return l;
}

这时通过反序列化能得到一个ArrayList对象;

Person p = new Person("张三", 20);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));

oos.writeObject(p);

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));

ArrayList a = (ArrayList)ois.readObject();

还有一个特殊方法:

ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;

该方法的返回值会代替原来反序列化的对象;原本由readObject()反序列化出来的对象会被直接丢弃;

另一种自定义序列化机制

JAVA另一种自定义序列化机制能由程序员决定存储和恢复对象数据,通过继承Externalizable接口:继承该接口会强制使用自定义序列化,也就是必须实现下面两个方法

void readExternal(ObjectInput in):

void writeExternal(ObjectOutput out):

其用法跟 writeObject和readObject一样

这篇关于JAVA序列化的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!