加入收藏 | 设为首页 | 会员中心 | 我要投稿 源码门户网 (https://www.92codes.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > MsSql教程 > 正文

JAVA序列化与反序列化内容

发布时间:2022-09-30 06:03:00 所属栏目:MsSql教程 来源:转载
导读: JAVA中,当我们想将某一个对象持久化到磁盘、Redis等等,又或者想在两个进程之间传递对象时,必须将对象进行序列化操作和反序列化操作。
序列化操作:就是将JAVA对象转化为可以在网络上传输

JAVA中,当我们想将某一个对象持久化到磁盘、Redis等等,又或者想在两个进程之间传递对象时,必须将对象进行序列化操作和反序列化操作。

序列化操作:就是将JAVA对象转化为可以在网络上传输,在磁盘上可以保存的二进制内容。

反序列化操作:就是将保存的二进制数据转化为JAVA对象,从而可以在JVM进行使用。

一、JAVA中进行序列化的方式:

在JAVA中有两个方式进行对象的序列化和反序列化。

1.实现Serializable接口。(大部分使用)

2.实现Externalizable接口。(它是Serialization接口的子类)

当我们的类没有实现Serializable接口又或者子类时,不能进行序列化或者反序列化操作,不然抛出一个异常。

二、Serializable接口

1.当我们为某一个类实现了Serializable接口。可以进行序列化和反序列化操作。

2.当进行序列化操作时:(将对象写入到磁盘中)

会先调用其writeReplace方法。

然后会调用其writeObject方法。

注意事项:

其中wirteReplace方法可以是从父类继承过来的,Object的返回值,无参,任何访问级别

而writeObject必须是本类的,并且是私有的,无返回值,ObjectOutputStream参数

writeReplace的方法签名:ObjectwriteReplace()。

writeObject的访达签名:private void writeObject(ObjectOutputStream)。

其中writeReplace方法给我们了一种选择,可以让我们可以在序列化的时候写入其他的对象,而不是当前对象。因为该方法有一个返回值,它会将返回值进行最终的持久化操作。

当我们没有重写writeObject方法时,JAVA会自动将该类的全部非transient属性进行持久化。当我们重写了writeObject方法时,并且是空实现,JAVA就只会将类对象进行持久化,而不会将该类的任何对象进行持久化,相当于一个空对象。这时需要在writeObject方法中将我们自己将需要的数据写入到ObjectOutputStream对象中,即传递的参数。

默认的writeObject不会序列化transient字段内容。

writeObject方法的作用:按需要对哪些对象属性进行序列化。这时候,我们可以手动将transient字段进行序列化。就会强制将transient进行序列化。

例如:

public class ExternalSerialTest implements Serializable {
    private int ss = 12;
    private String name;
    private String age = "age";
    public ExternalSerialTest() {
        System.out.println("11111111111111111111111111111111" );
    }
    static ExternalSerialTest externalSerialTest = new ExternalSerialTest();
    public static void main(String[] args) {
        externalSerialTest.name = "moxiao21212121";
        externalSerialTest.ss = 132;
        externalSerialTest.age = "age";
        try (FileOutputStream baos = new FileOutputStream("1.txt");
             ObjectOutputStream oos = new ObjectOutputStream(baos)) {
            oos.writeObject(externalSerialTest);
            oos.flush();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        try (FileInputStream bais = new FileInputStream("1.txt");
             ObjectInputStream ois = new ObjectInputStream(bais)) {
            Object o = ois.readObject();
            System.out.println(((ExternalSerialTest) o).name);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        System.out.println("readObject");
        int i = ois.readInt();
        String o = (String)ois.readObject();
        this.ss = i;
        this.name = o;
    }
    private void writeObject(ObjectOutputStream oos) throws IOException {
        System.out.println("writeObject");
        oos.writeInt(this.ss);
        oos.writeObject(this.name+"change");
        oos.flush();
    }
    private Object writeReplace() {
        System.out.println("writeReplace");
        return this;
    }
     Object readResolve() {
        System.out.println("readResolve");
        return this;
    }
}

执行结果:

11111111111111111111111111111111
writeReplace
writeObject
readObject
readResolve
moxiao21212121change

在上面的例子中,我们在writeObject中序列化name和ss两个字段,并在readObject修改了读取的name字段值。

在writeReplace方法中,直接返回了当前对象。

从上面输出可以看出:先执行writeReplace,然后执行writeObject方法。

3.当进行反序列化操作时:(将磁盘数据写入到对象中)

会先调用readObject方法

然后调用readResolve方法

注意事项:

其中readResolve方法可以是从父类继承过来的,Object的返回值,无参,任何方法访问级别

而readObject必须是本类的,并且是私有的,无返回值,ObjectInputStream参数

readResolve的方法签名:ObjectreadResolve()。

readObject的访达签名:private void readObject(ObjectInputStream)。

在进行反序列化操作时,生成新的JAVA对象时,是不会调用其构造函数的。

在进行反序列化操作时,生成新的JAVA对象,primitive都是默认值,其他对象都是null,不会执行属性上的默认值赋值。

其中readResolve方法,同样也给了一种选择机会,在进行反序列化操作时,最终返回的对象内容是什么,因为readResolve方法有一个Object返回值内容。

当我们重写了writeObject方法时,一定要重写readObject方法,不然会抛出一个java.io.StreamCorruptedException不正确的流内容异常。我们可以原样的从ObjectInputStream获取我们写入的数据内容。

从上面的例子可以看出来我们在readObject可读取了写入的数据内容,并追加了“change”内容。

最后会调用readResolve方法,让我们确定最终的返回对象

在序列化的时候,如果将一个对象序列化多次。在反序列化的时候,反序列化的对象也是同一个。这是因为每一个序列化的对象都有一个对应的序列化编码号。

序列化算法:

所有序列化对象都有一个序列化编号。

当JAVA试图序列化某一个对象时,会先检查此对象是否已经序列化过,只有此对象从未被序列化过,才会将此对象序列化为字节输出,如果已经序列化过,直接输出序列化的编号即可。

当反序列化时,如果反序列化为对象时,就会将反序列化的对象进行缓存起来。如果反序列化为序号时,直接从缓存中的获取。

所以:将一个对象序列化多次。反序列化时只有第一个是反序列对象,后面的都是从缓存中获取的mssql 使用序列,所以都是同一个对象。

4.补充说明:

上面的四个方法的顺序为:

writeReplace

writeObject

readObject

readResolve

这四个方法可以在ObjectStreamClass中找到定义:

writeReplaceMethod = getInheritableMethod(
                        cl, "writeReplace", null, Object.class);
readResolveMethod = getInheritableMethod(
                        cl, "readResolve", null, Object.class);
writeObjectMethod = getPrivateMethod(cl, "writeObject",
                        new Class<?>[] { ObjectOutputStream.class }, Void.TYPE);
readObjectMethod = getPrivateMethod(cl, "readObject",
                       new Class<?>[] { ObjectInputStream.class }, Void.TYPE);

三、Externalizable实现序列化:

他和Serializable一样,可以在序列化和反序列化的时候进行方法的调用,完成最终写入的数据内容。

实现Externalizable接口时,序列化的类必须存在一个public无参的构造函数。因为在反序列化的时候,需要调用构造函数创建对象。

会执行类的字段初始化。

他有两个抽象方法:writeExternal和readExternal方法。

serializable方法调用路径为:writeReplace->writeObject->readObject->readResolve

而Externalizable的调用路径为:writeReplace->writeExternal->readExternal->readResolve

其中第一和第四个方法是一样的。第二和第三个方法是Externalization的抽象方法。

serializable与externalizable区别:

在使用Eternalization进行反序列操作时,会调用默认的构造函数,如果没有的话,就会抛出异常。也会执行字段上默认值的赋值。而serialization进行反序列操作时,不会调用默认的构造函数。也不会执行字段上的默认值,即字段为primitive,有JAVA自带的默认值,int为0等等,而引用类型为null。

Externalizable的性能比Serializable稍好一些。

(编辑:源码门户网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!