Java

Java中序列化和反序列化以及关键字 transient

2018-05-08  本文已影响147人  小小少年Boy

总结

序列化的场景和作用:内存对象状态持久化、网络传输、远程方法调用传输对象

实现序列化方式:Serialization序列化接口、Externalizable外部序列化接口实现writeExtenal()和readExtenal()

序列化:对象输出流ObjectOutputStream和writeObject()
反序列化:对象输入流ObjectInputStream和readObject()

serialVersionUID:序列化版本标识的 静态final变量

Transient:阻止序列化
静态变量:不能被序列化


参考:
Java中的关键字 transient
java序列化与反序列化总结

详细说明

1、序列化和反序列化的概念

把Java对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为Java对象的过程称为对象的反序列化。

2、什么情况下需要序列化

当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
当你想用套接字在网络上传送对象的时候;
当你想通过RMI(远程方法调用)传输对象的时候;

3、如何实现序列化呢、使用场景?

Java提供两种对象持久化的方式,分别序列化和外部序列化。
序列化:实现Serialization接口(位于java.lang包中);
外部序列化:实现了Externalizable接口,Externalizable接口继承自Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为;

外部序列化与序列化的区别:

序列化是内置的API,只需要,只需要实现Serialization;
使用外部序列化时,接口必须由开发人员来实现,由于控制权交给了开发人员,在编程时有更多的灵活性,可能会提高性能。Externalizable接口提供了两个方法writeExternal()和readExternal()

对Serializable对象反序列化时,并不会调用任何构造函数 ,因此Serializable类无需默认构造函数,但是当Serializable类的父类没有实现Serializable接口时,反序列化过程会调用父类的默认构造函数,因此该父类必需有默认构造函数,否则会抛异常。

对Externalizable对象反序列化时,会先调用类的不带参数的构造方法 ,这是有别于默认反序列方式的。如果把类的不带参数的构造方法删除,或者把该构造方法的访问权限设置为private、默认或protected级别,会抛出java.io.InvalidException: no valid constructor异常,因此Externalizable对象必须有默认构造函数,而且必需是public的。

4、对象序列化和反序列化的步驟?

对象序列化包括如下步骤:  
1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;  
2) 通过对象输出流的writeObject()方法写对象。

对象反序列化的步骤如下:  
1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;  
2) 通过对象输入流的readObject()方法读取对象。

public class 序列化测试
{
    public static void main(String[] args) throws Exception
    {
        //序列化对象
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("d:/objectFile.data"));
        MyObject obj1 = new MyObject("liuhongliang", 24);
        out.writeObject("hello world!");
        out.writeObject(new Date());
        out.writeObject(obj1);
        out.writeInt(54);
        out.close();
        //反序列化对象 
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(("d:/objectFile.data")));
        System.out.println("obj1=" + (String) in.readObject());
        System.out.println("obj2=" + (Date) in.readObject());
        MyObject obj3 = (MyObject) in.readObject();
        System.out.println("obj3=" + obj3);
        int obj4 = in.readInt();
        System.out.println("obj4=" + obj4);
        in.close();
    }
}

//自定义可序列化类
class MyObject implements Serializable
{
    private String name;
    private int age;

    public MyObject(String name, int age)
    {
        this.name = name;
        this.age = age;
    }

    public String toString()
    {
        return "name=" + name + ", age=" + age;
    }
}

5、serialVersionUID是什么?

总结:序列化和反序列化的版本控制
凡是实现Serializable接口的类都应当有一个表示序列化版本标识符的静态变量:

private static final long serialVersionUID;

当我们的类实现了Serializable接口后,会有一个警告,告诉你需要生成一个serialVersionUID属性。这个serialVersionUID是做什么用的呢?其实这是JAVA序列化的版本控制功能。当序列化对象时会把这个属性写入,当反序列化时则会把这个属性取出,然后与JAVA类中的serialVersionUID属性值对比,如果一致,则认为是同一个版本,正常反序列化,如果不一致则认为版本不同,抛出InvalidClassException异常。

6、Transient关键字

参考:Java transient关键字使用小记

作用:就是让某些被修饰的成员属性变量不被序列化;

用途:一些敏感信息,比如银行密码账号之类的,不希望网络传输或者磁盘持久化,使用transient关键字,这个字段的生命周期仅存与调用者的内存而不会写入磁盘里持久化

注意:
Transient修饰的变量和静态变量都不能被序列化;
一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问;

在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。

例如:HashMap中的modCount变量、StringBuffer用于存储的字符数组char[] toStringCache

7、Serializable接口中有几个方法?如果没有方法的话,那么这么设计Serializable接口的目的是什么?

Serializable接口在java.lang包中,是Java序列化机制的核心组成部分。它里面没有包含任何方法,我们称这样的接口为标识接口。如果你的类实现了Serializable接口,这意味着你的类被打上了“可以进行序列化”的标签,并且也给了编译器指示,可以使用序列化机制对这个对象进行序列化处理。

8、在Java进行序列化和反序列化处理的时候,哪些方法被使用了?

这个是面试中常见的问题,主要用来考察你是否对readObject()、writeObject()、readExternal()和writeExternal()方法的使用熟悉。

Java序列化是通过java.io.ObjectOutputStream这个类来完成的。这个类是一个过滤器流,这个类完成对底层字节流的包装来进行序列化处理。

通过ObjectOutputStream.writeObject(obj)进行序列化
通过ObjectInputStream.readObject()进行反序列化。

对writeObject()方法的调用会触发Java中的序列化机制。readObject()方法用来将已经持久化的字节数据反向创建Java对象,该方法返回Object类型,需要强制转换成你需要的正确类型。

9、假设一个新的类的超类实现了Serializable接口,那么如何让这个新的子类不被序列化?

重新实现writeObject()和readObject()方法,并在方法实现中通过抛出NotSerializableException

上一篇 下一篇

猜你喜欢

热点阅读