文章目录

  • 前言
  • 对象流
  • 序列化:
  • 序列化的特点:
  • serialVersionUID:序列化版本号
  • 序列化的过程
  • 反序列化:
  • 反序列化的过程:
  • 补充:



前言

使用对象和磁盘打交道的IO类是ObjectInputStream和ObjectOutputStream
而序列化和反序列化是对象是否可以使用对象流的关键




对象流

ObjectInputStream:对象输入流:存入到磁盘中一个对象实体
ObjectOutputStream:对象输出流:从瓷盘中取出一个对象实体
注意:对象流操作的对象需要 实现序列化。

序列化:

将对象实例保存到磁盘的过程就是序列化(ObjectOutputStream)

序列化的特点:

1、对象要能完成序列化和反序列化的对象必须实现Serializable接口
2、序列化和反序列化需要使用到IO类ObjectInputStream和ObjectOutputStream
3、虚拟机是否能支持序列化,也需要取决于serialVersionUID值
4、transient关键字是来控制变量的序列化,在变量前加关键,当前的变量不参与序列化,反序列化中 transient关键字修饰的变量会给定初始值
如:int类型0,对象类型给定null值
5、要将父类对象也参与序列化,则就需要让父类对象实现实现Serializable接口

serialVersionUID:序列化版本号

serialVersionUID是一个标识符,通常在对象的哈希码序列化时会标记在对象上
serialVersionUID生成方式:
1、一般可以是默认值1
2、另一种是类名、接口名、成员方法和属性等生成的64位的哈希字段
作用是进行版本控制,当对类添加或者修改类中的任何字段,已经序列化的对象将无法恢复,因为类已经生成新的serialVersionUID,在进行反序列化时,会将当前类的serialVersionUID和序列化的对象的serialVersionUID进行比较,如果不匹配,将抛出异常

序列化的过程

ObjectOutputStream outputStream = null;
        try {
            outputStream = new ObjectOutputStream(new FileOutputStream(path));
            outputStream.writeObject(o);

            outputStream.flush();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

反序列化:

将对象从磁盘中读取出来的过程是反序列化(ObjectInputStream)

反序列化的过程:

ObjectInputStream inputStream = null;
        try {
            inputStream = new ObjectInputStream(new FileInputStream(path));
            Person person = (Person) inputStream.readObject();
            System.out.println(person);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

补充:

为什么Serializable接口没有任何方法可以完成序列化:
我们通过查看ArrayList源码来进行分析
在序列化过程中,如果被序列化的类实现了writeObject和readObject方法,虚拟机在调用对象类型的readObject 和writeObject是,会执行到该类中的自定义的方法,如果类里面没实现自定义的方法的话,ObjectOutputStream会调用默认的defaultWriteObject,或者ObjectInputStream中的defaultInputStream方法

if (obj instanceof String) {
                writeString((String) obj, unshared);
            } else if (cl.isArray()) {
                writeArray(obj, desc, unshared);
            } else if (obj instanceof Enum) {
                writeEnum((Enum) obj, desc, unshared);
            } else if (obj instanceof Serializable) {
            //判定当前是Serializable实现类
                writeOrdinaryObject(obj, desc, unshared);
            } else {
                if (extendedDebugInfo) {
                    throw new NotSerializableException(
                        cl.getName() + "\n" + debugInfoStack.toString());
                } else {
                    throw new NotSerializableException(cl.getName());
                }
            }
Serializable接口提供一个标识,方便底层序列化中调用特定的方法

transient:的作用:
比如:ArrayList中的elementDate属性使用transient关键字修饰
ArrayList是一个动态数组,每次在放满后都会动态的扩容设定一个长度值,如果设定数据长度为100,而实际存放1个有效数据,那么会序列化99个null元素,为了保证序列化不将99个null值参与序列计划,ArrayList设定为transient
elementDate在序列化是是将数据取出来进行序列化的