Java
语言支持一种称为对象序列化(Object
Serialization)的非常通用的机制,可以将任何对象写入到流中,并在之后将其读回,首先需要支持对象序列化的类,必须继承与
Serializable
接口,该接口没有任何方法,只是对类起到标记的作用,然后使用
ObjectOutputStream
流来序列化对象,使用
ObjectInputStream
流来反序列化,示例代码如下:

  • 对象类声明:

    public class Employee implements Serializable {

            private String name;

            private String sex;

            public
    Employee() {

            }

            public
    Employee(String name, String sex) {

                    this.name = name;

                    this.sex = sex;

            }

            // getter 和 setter 方法

    }

     
     

    public class Manager extends Employee {

            private Employee secretary;

            public
    Manager(){

            }

            public
    Manager(String name, String sex) {

                    super(name, sex);

            }

            // getter 和 setter 方法

    }

  • 创建对象实例:

     Employee harry = new
    Employee("Harry Hacker", "男");

     Manager boss = new
    Manager("Carl Cracker", "女");

    boss.setSecretary(harry);

  • 序列化到文件

     
    ObjectOutputStream outputStream = null;

             try {

                   
    outputStream = new
    ObjectOutputStream(new
    FileOutputStream("serializableApp.dat"));

                    outputStream.writeObject(boss);

            
    } catch (FileNotFoundException ex) {

                    ex.printStackTrace();

            
     } finally {

                    if (outputStream != null) {

                           outputStream.close();

                    }

           
     }

  • 从文件反序列化

                ObjectInputStream inputStream = null;

                try {

                        inputStream = new
ObjectInputStream(new
FileInputStream("serializableApp.dat"));

                        Manager serializableBoss = (Manager) inputStream.readObject();

                        System.out.println("manager name is " + serializableBoss.getName() + " sex is "

                                        + serializableBoss.getSex() + " secretary is "

                                        + serializableBoss.getSecretary().getName());

                } catch (ClassNotFoundException ex) {

                        ex.printStackTrace();

                } catch (FileNotFoundException ex) {

                        ex.printStackTrace();

                } finally {

                        if (inputStream != null) {

                                inputStream.close();

                        }

                }

每个对象都用一个序列号保存的,对象序列化机制如下:

  • 对于遇到的每一个对象引用都关联一个序列号
  • 对于每一个对象,当第一次遇到时,保存器对象数据到流中
  • 如果某个对象已经被保存过,那么只写出保存的序列号
  • 对于流中的对象,在第一次遇到其序列号时,创建他,并使用流中数据来初始化他,然后记录这个顺序号和新对象之间的关联
  • 当遇到对象引用另一个对象的序列号时,获取与这个序列号相关联的对象引用

某些数据域时不可以序列化的,例如,只对本地方法有意义的存储文件句柄或窗口句柄的整数值等,Java
拥有一种简单的机制来防止这种域被序列化,那就是将他们标记成
transient,如果被标记为不可序列化的类,也需要将其标记为
transient,瞬时域在对象序列化时总是被跳过,示例如下:

    private transient Point2D.Double point;

 
 

  1. 修改默认的序列化机制

    序列化机制单个的类提供了一种方式,去向默认的读写行为添加验证或任何其他想要的行为,可序列化类可以定义具体有如下签名的方法:

            private
    void
    readObject(ObjectInputStream in)

                            throws IOException,ClassNotFoundException;

            
     

            private
    void
    writeObject(ObjectOutputStream out)

                            throws IOException;

    readObject

    writeObject
    方法只需要保存和加载本类的数据域,而不需要关注基类(超类)数据和任何其他类的信息,实现给方法的具体示例如下:

         private
    void
    readObject(ObjectInputStream in)

                            throws IOException, ClassNotFoundException {

                    // 读取序列化字段数据

                    in.defaultReadObject();

                    // 其他数据校验或者序列化

            }

            private
    void
    writeObject(ObjectOutputStream out)

                            throws IOException{

                    // 写入序列化字段数据

                    out.defaultWriteObject();

                    // 其他数据校验或者序列化

           }

    除了可以使用readObject

    writeObject方法来保存和恢复对象数据外,类还可以定义他自己的机制,类需要实现
    Externalizable
    接口,该接口定义了两个方法:

        public
    void
    readExternal(ObjectInput in)

            
    throws IOException, ClassNotFoundException;

     
     

        public
    void
    writeExternal(ObjectOutput out)

                throws IOException ;

    这些方法对包括超类数据在内的整个对象的存储和恢复负全责,而序列化机制在流中仅仅只是记录该对象所属的类,示例代码如下:

  • 基类代码:

            public
    void
    writeExternal(ObjectOutput out) throws IOException {

                    out.writeUTF(this.name);

                    out.writeUTF(this.sex);

            }

            public
    void
    readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

                    this.name = in.readUTF();

                    this.sex = in.readUTF();

            }

  • 子类代码:

    @Override

    public
    void
    readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

            super.readExternal(in);

            this.secretary = new
    Employee();

           
    this.secretary.readExternal(in);

     }

    @Override

    public
    void
    writeExternal(ObjectOutput out) throws IOException {

            super.writeExternal(out);

           this.secretary.writeExternal(out);

     }

     
     

  1. 序列化单例和类型安全的枚举

    在序列化和反序列化时,如果目标对象时唯一的,使用默认的序列化机制时不适用的,因为默认的序列化机制,即使构造器时私有的,序列化机制也可以创建新的对象,因此在进行==(比较)时将失败,为了解决整个问题需要定义一个名称为 readResolve的特殊方法,在对象被序列化之后就会调用他,返回一个对象,而该对象之后会称为 readObject 的返回值,示例代码如下:

    public class Orientation implements Serializable {

            public static final Orientation HORIZONTAL = new
    Orientation(1);

            public static final Orientation VERTICAL = new
    Orientation(2);

     
     

            private
    int value;

     
     

            private
    Orientation(int value) {

                    this.value = value;

            }

     
     

            protected Object readResolve() throws ObjectStreamException {

                    if (value == 1) {

                            return HORIZONTAL;

                    }

                    if (value == 2) {

                            return VERTICAL;

                    }

                    
     

                    return null;

            }

    }

  2. 版本管理

    无论类的定义产生了什么样的变化,他的SHA指纹也会跟着变化,而我们知道对象流拒绝读入具有不同指纹的对象,但是,类可以表明他对其早期版本保持兼容,在类的所有较新的版本都必须把
    serialVersionUID
    常量定义与最初版本的指纹相同,如果一个类具有名为
    serialVersionUID
    的静态数据成员,就不需要在人工的计算其指纹,而只需直接使用整个值,示例如下:

    public class Employee implements Serializable, Externalizable {

            public static final
    long serialVersionUID = -2349238498234324L;

    }

    如果类只有方法产生了变化,那么在读入新对象数据时是不会有任何问题的,如果是数据域产生了变化,那么就可能会有问题,常见情况如下:

  • 如果数据域之间名字匹配而类型不匹配,那么对象流不会进行类型转换,因此不兼容
  • 如果流中的对象具有当前版本中所没有的数据域,那么对象流会忽视这些额外的数据
  • 如果当前版本具有在流化对象中所没有的数据域,那么这些新增加的域将被设置成他们的默认值(对象是null、数字为
    0,布尔类型为
    false)

 
 

笔记:I/O流-对象序列化的更多相关文章

  1. Java开发笔记(九十)对象序列化及其读写

    有些时候,开发者想把程序运行过程中的数据临时保存到文件,可是前面介绍的字符流和字节流,要么用来读写文本字符串,要么用来读写字节数组,并不能直接保存某个对象信息,因为对象里面包括成员属性和成员方法,单就 ...

  2. Java学习笔记——IO操作之对象序列化及反序列化

    对象序列化的概念 对象序列化使得一个程序可以把一个完整的对象写到一个字节流里面:其逆过程则是从一个字节流里面读出一个事先存储在里面的完整的对象,称为对象的反序列化. 将一个对象保存到永久存储设备上称为 ...

  3. java学习笔记之IO编程—对象序列化

    对象序列化就是将内存中保存的对象以二进制数据流的形式进行处理,可以实现对象的保存或网络传输. 并不是所有的对象都可以被序列化,如果要序列化的对象,那么对象所在的类一定要实现java.io.Serial ...

  4. JAVA笔记12__字节、字符缓冲流/打印流/对象流/

    /** * !!:以后写流的时候一定要加入缓冲!! * 对文件或其它目标频繁的读写操作,效率低,性能差. * 缓冲流:好处是能更高效地读写信息,原理是将数据先缓冲起来,然后一起写入或读取出来. * * ...

  5. javaSE学习笔记(15) ---缓冲流、转换流、序列化流

    javaSE学习笔记(15) ---缓冲流.转换流.序列化流 缓冲流 昨天复习了基本的一些流,作为IO流的入门,今天我们要见识一些更强大的流.比如能够高效读写的缓冲流,能够转换编码的转换流,能够持久化 ...

  6. I/O 流和对象序列化

    一.I/O 流(java 如何实现与外界数据的交流) 流定义: 任何有能力产出数据的数据源对象或者有能力接收数据的数据源对象.他屏蔽了实际的I/O设备处理数据的细节. 1.Input/Output:指 ...

  7. JAVA基础学习day22--IO流四-对象序列化、管道流、RandomAccessFile、DataStream、ByteArrayStream、转换流的字符编码

    一.对象序列化 1.1.对象序列化 被操作的对象需要实现Serializable接口 1.2.对象序列化流ObjectOutputStream与ObjectInputStream ObjectInpu ...

  8. 疯狂Java学习笔记(84)----------大约 Java 对象序列化,你不知道 5 事

    几年前,.当一个软件团队一起用 Java 书面申请.我认识比一般程序猿多知道一点关于 Java 对象序列化的知识所带来的优点. 关于本系列 您认为自己懂 Java 编程?其实,大多数程序猿对于 Jav ...

  9. Java IO(Properties/对象序列化/打印流/commons-io)

    Java IO(Properties/对象序列化/打印流/commons-io) Properties Properties 类表示了一个持久的属性集.Properties 可保存在流中或从流中加载. ...

随机推荐

  1. Windows平台监听服务无法启动报报TNS-12560 TNS-00530案例

      在Windows Server 2012平台使用命令启动监听服务时遇到了TNS-12560 & TNS-00530错误. C:\Users>lsnrctl start GEW_LIS ...

  2. The Windows account sa does not exist and cannot be provisioned as a SQL Server system administrator

    今天遇到一个案例,在使用命令修改一个测试服务器(SQL Server 2014标准版)的服务器排序规则时,遇到了下面错误信息 (具体账号信息脱敏处理,随机生成一个账号密码) The Windows a ...

  3. Mongodb3.0.5副本集搭建及spring和java连接副本集配置

    这是去年写的一篇文档,最近突然发现并没有发不出来,因此现在补上,希望能对某些朋友有所帮助.因为当时记录时没有截图,因此这里看起来可能就比较单调. 一.基本环境: mongdb3.0.5数据库 spri ...

  4. WebService之CXF注解之四(测试类)

    TeacherTest.java: /** * @Title:TeacherTest.java * @Package:com.test.service * @Description: * @autho ...

  5. Caused by: java.lang.NoClassDefFoundError: org/hibernate/cfg/Configuration

    1.错误描述 usage: java org.apache.catalina.startup.Catalina [ -config {pathname} ] [ -nonaming ] { -help ...

  6. Android APP开发入门教程-Button

    代码编写 做好准备工作后,终于可以开始写我们的hello android了,在开始编写代码之前,我们先了解几个文件: res/layout/main.xml App主窗体布局文件,你的应用长什么样都在 ...

  7. idea好用插件(一)

    代码规范插件 Alibaba Java Coding Guidelines 安装后 可以在文件.文件夹邮件,显示编码规约扫描,点击后显示 可以通过双击定位问题代码,对某些问题可以进行快速的修复 比如: ...

  8. js小括号的作用

    js中小括号()的用法详解:对于小括号无论是菜鸟还是高手一定都不会陌生,可以说它几乎是随处可见,虽然熟悉但并非真正的理解,由此可能会产生很多莫名其妙的错误,下面就通过代码实例详细介绍一下小括号的用法. ...

  9. Asp.net mvc 5 razor

    一开始学习dotnet的web项目是Asp.net webform,完全不理解项目为什么要这样设计,就简单的使用ajax调用后台的代码不好吗?为什么还要搞一些什么代码后置的东东. 还有就是有各种加载问 ...

  10. [COGS2701]:动态树

    题面 传送门 Sol LCT维护子树和 # include <bits/stdc++.h> # define IL inline # define RG register # define ...