ObjectStreamDemo

当你需要存储相同类型的数据时,使用固定长度的记录格式是一个不错的选择。但,在OOP中创建的对象很少全部都具有相同的类型。

例如,你可能有一个称为staff(见下面demo)的array,它名义上是一个Employee记录数组,但是实际上却包含诸如Manager这样的子类实例。

为解决这个问题,当然可以设计出一种数据格式来存储这种多态集合,但由于有序列化,设计这种类型是没有必要的。
Java语言支持一种称为对象序列化(Object serialization)的非常通用的机制,它可以将任何对象写出到流中,并在之后将其读回。

如果需要对象流中存储或恢复的所有类都进行一下修改,这些类必须实现Serializable接口;
Serializable接口没有任何方法,与Cloneable接口很相似。

只有在写出对象时才能用writeObject/readObject方法,对于基本类型值,需要使用诸如writeInt/readInt或writeDouble/readDouble这样的方法。对象流类都实现了DataInput/DataOutput接口。

由于对象引用而形成的对象网络,Object Serialization不能去保存和恢复对象的地址,因为当对象被重新加载时,JVM为对象分配的内存地址与在另一个JVM中分配的不同。
Object Serialization在处理的办法是使用序列号(serial number):
每个对象都是用一个序列号保存。由于序列化使用序列号代码内存地址,所以允许将对象集合从一台机器传送到另一台机器。正是由于序列号的使用,这种机制被称为对象序列化。
其算法如下:
(1)对遇到的每一个对象引用都关联一个序列号
(2)对于每个对象,当第一次遇到时,保存其对象数据到流中
(3)如果某个对象之前已经被保存过,那么只写出“与之前保存过的序列号为x的对象相同”;在读回对象时,整个过程是反过来的
(4)对于流中的对象,在第一次遇到其序列号时,构建它,并使用流中数据来初始化它,然后记录这个顺序号和新对象之间的关联
(5)当遇到“与之前保存过的序列号为x的对象相同”标记时,获取与这从此顺序号相关联的对象引用

例外的处理:
某些数据域永远都不应该被序列化,例如,只对本地访问有意义的存储文件句柄或窗口句柄的整数值,这种信息在稍后重新加载对象或将其传送到其他机器上时都是没有用处的。事实上,这种域的值如果不恰当,还会引起本地方法崩溃。
Java有一种很简单的机制来防止这种域被序列化,那就是将这些域使用transient关键字来修饰。如果这些域属于不可序列化的类,使用transient关键字修饰即可。瞬时的域在对象被序列化时问题被跳过的。
例如java.awt.geom包中有大量类都是不可能序列化的,例如Point2D.Double,在对象定义使用private transient Point2D.Double point;这样就可以避免NotSerializableException;

在序列化和反序列化对象被认为是惟一时,需要对枚举值做特殊处理。唯一的对象,譬如单例和类型安全的枚举

    protected Object readResolve() {
        if (value==1) {
            return Orientation.HORIZONTAL;
        }else if (value==2) {
            return Orientation.VERITAL;
        }
        return null;
    }
package io.serial;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Date;
import java.util.GregorianCalendar;

/*2015-7-9*/
public class ObjectStreamTest {
    public static void main(String[] args) {
        Employee harry = new Employee("Tom", 20000, 2015, 7, 10);
        Manager carl = new Manager("Carl Cracker", 80000, 1987, 12, 15);
        carl.setSecretary(harry);
        Manager tony = new Manager("Tony Tester", 40000, 1990, 3, 15);
        tony.setSecretary(harry);

        Employee[] staff = new Employee[3];
        staff[0] = harry;
        staff[1] = carl;
        staff[2] = tony;

        try {
            String fileName = "employee.dat";
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(fileName));
            out.writeObject(staff);
            out.close();

            ObjectInputStream in = new ObjectInputStream(new FileInputStream(fileName));
            Employee[] newStaff = (Employee[]) in.readObject();
            for (Employee employee : newStaff) {
                System.out.println(employee);
            }
            in.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }

}

class Employee implements Serializable {
    private static final long serialVersionUID = -167978670073609475L;
    private String name;
    private double salary;
    private Date hireDay;

    public Employee(String name, double salary, int year, int month, int dayOfMonth) {
        super();
        this.name = name;
        this.salary = salary;
        GregorianCalendar calendar = new GregorianCalendar(year, month - 1, dayOfMonth);
        this.hireDay = calendar.getTime();
    }

    public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }

    public Date getHireDay() {
        return hireDay;
    }

    public void raiseSalary(double byPercent) {
        double raise = salary * byPercent / 100;
        salary += raise;
    }

    @Override
    public String toString() {
        return getClass().getName() + " [name=" + name + ", salary=" + salary + ", hireDay=" + hireDay + "]";
    }

}

class Manager extends Employee {
    private static final long serialVersionUID = -668252664793507835L;
    private Employee secretary;

    public Manager(String name, double salary, int year, int month, int dayOfMonth) {
        super(name, salary, year, month, dayOfMonth);
    }

    public void setSecretary(Employee secretary) {
        this.secretary = secretary;
    }

    @Override
    public String toString() {
        return super.toString() + " [secretary=" + secretary + "]";
    }
}

Output:

io.serial.Employee [name=Tom, salary=20000.0, hireDay=Fri Jul 10 00:00:00 CST 2015]
io.serial.Manager [name=Carl Cracker, salary=80000.0, hireDay=Tue Dec 15 00:00:00 CST 1987] [secretary=io.serial.Employee [name=Tom, salary=20000.0, hireDay=Fri Jul 10 00:00:00 CST 2015]]
io.serial.Manager [name=Tony Tester, salary=40000.0, hireDay=Thu Mar 15 00:00:00 CST 1990] [secretary=io.serial.Employee [name=Tom, salary=20000.0, hireDay=Fri Jul 10 00:00:00 CST 2015]]
时间: 2024-10-29 19:12:35

ObjectStreamDemo的相关文章

Java核心类库-IO-对象流(实现序列化与反序列化)

使用对象流来完成序列化和反序列化操作: ObjectOutputStream:通过writeObject()方法做序列化操作的 ObjectInputStream:通过readObject方法来做反序列化操作的 做序列化操作必须存在对象的字节码对象. 1 public class ObjectStreamDemo { 2 public static void main(String[] args) throws Exception { 3 4 File file = new File("obj.

IO包中的其他类总结

一.PrintStream和PrintWriter PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式. PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节. 在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类. 1 /** 2 * PrintStream 3 * 1.提供了打印方法,可以对多种类型值进行打印,并保持数据的原有格式 4 * 2.它不抛IOException 5 * 6 * 构造函数 接受三种类型的值

java 21 - 13 IO流之序列化和反序列化

序列化流:把对象按照流一样的方式存入文本文件或者在网络中传输.对象 -- 流数据(ObjectOutputStream) 构造方法:ObjectInputStream(InputStream in)  创建从指定 InputStream 读取的 ObjectInputStream 反序列化流:把文本文件中的流对象数据或者网络中的流对象数据还原成对象.流数据 -- 对象(ObjectInputStream) 构造方法:ObjectInputStream(InputStream in)  创建从指定

5. IO流:★★★★★

IO流:★★★★★,用于处理设备上数据. 流:可以理解数据的流动,就是一个数据流.IO流最终要以对象来体现,对象都存在IO包中. 流也进行分类: 1:输入流(读)和输出流(写). 2:因为处理的数据不同,分为字节流和字符流. 字节流:处理字节数据的流对象.设备上的数据无论是图片或者dvd,文字,它们都以二进制存储的.二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节.意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据. 那么为什么要有字符流呢?因

ObjectOutputStream&ObjectInputStream--对象流

用于操作对象的流对象,对象的序列化ObjectInputStream ObjectOutputStream特点:用于操作对象.解决的问题:将对象持久化到硬盘功能:特有:writeObject(); import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputS

[javaSE] IO流(对象序列化)

写入 获取ObjectOutputStream对象,new出来,构造参数:FileOutputStream对象目标文件 调用ObjectOutputStream对象的writeObject()方法,参数:要保存的对象 调用ObjectOutputStream对象的close()方法,关闭流 此时会报异常,NotSerialzeableException,是因为目标类没有实现Serializable接口,这个接口没有方法,称为标记接口,会在改变类之后,生成新的序列号,保存的文件读取时会显示错误信息

黑马程序员------IO(五)

黑马程序员------IO(五) 1.1  操作对象(示例1)ObjectInputStream与ObjectOutputStream 被操作的对象需要实现Serializable. Serializable:用于给被序列化的类加入ID号,用于判断类和对象是否是同一个版本 类通过实现java.io.Serializable接口以启用序列化功能,Serializable只是一个标记接口. 1 示例1: 2 import java.io.*; 3 4 class ObjectStreamDemo 5

IO流(四):其他流

一.操作基本数据类型的流 (一)构造方法 1.数据输入流:DataInputStream(InputStream in) 2.数据输出流:DataOutputStream(OutputStream out) (二)方法 1.DataOutputStream: writeByte(10); writeShort(100); writeInt(1000); writeLong(10000); writeFloat(12.34F); writeDouble(12.56); writeChar('a')

黑马程序员——IO篇

------- android培训.java培训.期待与您交流! ---------- IO(Input Output)流 1.IO流用来处理设备之间的数据传输 2.Java对数据的操作是通过流的方式 3.Java用于操作流的对象都在IO包中 4.流按操作数据分为两种:字节流与字符流 . 字符流的数据字节流其实都能处理,因为无论什么在计算机上最后都是以字节存在的,但是其中有一部分数据它是以文本形式存在的,所以为了方便有了字符流,字符流里融合了编码表,而只有文字用到了编码表,所以字符流用来处理文本