速 溶 用JavaBeans

用 对 象 序 列 化 实 现bean 的 持 久 性


---- 摘 要:

---- 构 造 灵 活 易 变 的 软 件 构 件 是 所 有 软 件 构 件 技 术 的 关 键 部 分,JavaBeans 构 件 技 术 也 不 例 外。 如 果 将 构 件 序 列 化 之 后 再“ 重 建” 软 件 构 件, 则 会 大 大 增 加 系 统 设 计 人 员 的 自 由 度 和 设 计 能 力。 本 文 将 讨 论 为 什 么 要 使 软 件 构 件 具 有 持 久 性, 并 且 构 造 出 一 个 持 久 的JavaBeans。

---- 在 此 以 前, 我 们 所 做 的 关 于JavaBeans 的 讨 论, 只 涉 及 了beans 在 单 个JAVA 运 行 程 序 中 的 优 点。 这 些JavaBeans 仅 当 调 用 它 的 程 序 正 在 执 行, 并 且 对 它 有 激 活 的 引 用 时 才 存 在。 然 而, 我 们 希 望 软 件 构 件 能 够 在 调 用 它 的 程 序 结 束 后 继 续 存 在, 当 程 序 被 重 新 激 活 时 它 能 复 活 并 再 次 运 行; 或 者 它 能 在 机 器 之 间 移 动, 同 时 收 集 信 息, 或 执 行 远 程 服 务。 要 达 到 上 述 目 的, 构 件 的 持 久 性 是 关 键。

---- 当 程 序 终 止, 或 者 对Beans 的 引 用 全 部 结 束 时, 所 有Beans 的 状 态 信 息(Bean 中 的 值) 都 将 永 远 丢 失。 除 非 我 们 已 经 存 储 了Beans 中 足 够 多 的 信 息 以 便 将 来 能 够 重 建 它。 实 现 软 件 对 象 的 持 久 性 就 是 把 对 象 的 信 息 保 存 下 来 以 便 于 在 不 同 的 时 间 或 地 点 能 够 重 建 对 象。 对 象 序 列 化 是 实 现 持 久 性 的 方 法 之 一, 它 把 对 象 的 状 态 转 换 成 一 个 字 节 流, 利 用 这 些 信 息 可 以 重 建 与 原 始 对 象 完 全 一 致 的 拷 贝。

---- 在 本 文 中, 我 们 将 讨 论 持 久 性 机 制 对 软 件 构 件 结 构 的 意 义 及 其 目 标 并 给 出 一 些 介 绍 性 的 持 久JavaBeans 代 码 的 例 子。

一 种 简 单 的 存 贮

---- 虽 然 对 象 的 持 久 性 听 起 来 象 是 一 个 新 概 念, 但 你 很 有 可 能 早 已 非 常 熟 悉 它 了。 存 贮 在 硬 盘 或 软 盘 上 的 每 一 个 文 件 都 可 看 成 某 类 持 久 性 软 件 对 象。 举 例 来 说, 如 果 你 想 用 一 个 文 本 编 辑 器 建 立 文 本 文 件, 在 计 算 机 的 内 存 中 有 一 个 数 据 结 构, 其 中 包 含 了 所 编 辑 的 文 本, 当 用“Save as” 命 令 存 盘 时, 你 实 际 上 是 告 诉 程 序 把 内 存 中 的 内 容 持 久 地 保 存 到 磁 盘 文 件 中。

---- 当 你 下 次 运 行 文 本 编 辑 器 并 载 入 文 件 时, 编 辑 器 读 入 文 本 文 件 中 的 信 息, 并 且 在 内 存 中 建 立 一 个 结 构, 该 结 构 中 心 内 容 与 最 后 一 次 保 存 文 件 的 内 存 中 心 内 容 几 乎 相 同。 用“ 几 乎” 这 个 词 是 因 为 一 般 来 说, 不 是 软 件 对 象 的 所 有 信 息 都 要 保 存。 例 如, 在 文 本 编 辑 器 中 可 能 有 一 些“Undo” 信 息, 当 关 闭 编 辑 器 时 这 些 信 息 也 就 没 了。 当 你 再 次 启 动 文 本 编 辑 器 时, 文 件 的 内 容 还 在, 但 是 那 些 “Undo” 信 息 已 被 抛 到 了 九 霄 云 外。

对 象 持 久 和 序 列 化 的 工 作 机 理

---- 软 件 对 象 的 持 久 机 制 几 乎 与 上 面 的 文 件 保 存 例 子 一 模 一 样: 面 向 对 象 系 统 中 的 软 件 对 象 能 被 序 列 化, 可 被 转 换 为 字 节 流, 该 序 例 化 的 字 节 流 少 用 来 在 其 他 时 间 或 地 点 重 建 对 象。 开 发 上 述 文 本 编 辑 器 的 编 程 员, 很 有 可 能 把 文 本 处 理 为 单 独 的 对 象, 它 可 以 是 一 个“ 完 全 的” 包 容 一 切 的 对 象, 也 可 以 是 由 许 多 较 小 的 对 象 组 成 的 集 合, 这 些 较 小 的 对 象 的 功 能 是 完 成 各 项 序 列 化 的 任 务。 文 本 对 象 的 序 列 化 状 态 一 个 可 能 很 长 的 字 符 串 的 形 式 返 回, 并 放 在 磁 盘 上 以 备 后 用。 当 用 户 要 求 打 开 一 个 文 件 时, 程 序 打 开 文 件, 读 入 串 并 把 它 递 交 给 文 本 类( 或 者 其 他 能 够 建 立 文 本 对 象 的 类), 这 样, 文 本 对 象 就 复 活 了。

---- 当 程 序 命 令 文 本 对 象 进 行 序 列 化 时, 文 本 可 能 返 回 一 个 长 长 的 插 入 了 新 行 的ASCII 字 符 串 , 当 它 被 直 接 送 到 打 印 机 上 时, 就 是 可 读 的 了。 但 是 文 本 可 能 也 返 回 一 串 压 缩 了 的, 难 以 辨 认 的 乱 码, 你 永 远 没 法 理 解, 然 而 文 本 对 象 却 能 够“ 理 解” 并 用 它 来 重 建 文 本 的 实 例。 这 一 点 很 重 要: 序 列 化 成 串 的 对 象 能 够 原 样 恢 复。 类 的 对 象 写 下 一 个 串, 并 能 在 后 来 接 受 这 个 串。 否 则 的 话 就 没 法 实 现 持 久 性。

---- 一 般 来 讲, 对 象 序 列 化 工 作 的 具 体 实 现 方 式 并 不 确 定。( 具 体 的 构 件 技 术 决 定 了 序 列 化 对 象 的 格 式。) 这 就 是 为 什 么 任 何 一 个 合 理 的 字 处 理 器 都 提 供 了 许 多 文 件 格 式 供 你 选 择。 不 同 的 字 处 理 器 用 不 同 的 方 法 序 列 化 对 象, 但 因 为 它 们 又 都 是 文 本, 有 一 些 共 同 的 特 性, 如 字 符、 字 体、 段 落、 标 题 等 等, 所 以 软 件 对 象 有 可 能 理 解 彼 此 的 文 件 格 式 和 操 作。 类 似 地, 软 件 对 象 能 读 写 彼 此 的 序 列 化 格 式, 并“ 模 拟” 成 对 方 的 实 例。 我 们 将 在 后 面 详 细 讨 论 这 一 点。

---- 已 被 浓 缩 为 串 的 对 象 接 下 来 就 可 以 传 送, 存 贮, 并 且 可 以 象 操 作 串 一 样 对 其 进 行 操 作。 把 对 象 按 串 来 存 贮 给 系 统 设 计 人 员 带 来 很 大 的 自 由。 例 如, 假 定 你 在 为 一 个 数 据 库 应 用 编 写 一 个 图 形 用 户 接 口, 并 已 经 完 成 了 一 个 非 常 漂 亮 构 件, 该 构 件 用 来 操 纵 特 定 的 表 或 查 询 结 果。 你 可 以 把 这 个 针 对 客 户 要 求 设 计 的 构 件 序 列 化, 并 存 在 数 据 库 里 面 假 设 存 在 一 个 叫 做EDITORS 的 表 里。 然 后 你 就 可 以 用 这 些 编 辑 构 件 组 织 你 的 数 据 库 应 用 的 用 户 接 口, 每 一 种 构 件 只 能 操 作 一 特 定 的 数 据。 例 如, 为 了 修 改 用 来 编 辑 某 个 表 的 屏 幕 显 示, 你 只 需 修 改EDITORS 表 中 相 关 的 序 列 化 编 辑 构 件, 最 终 用 户 的 应 用 将 会 自 动 用 新 的 构 件 实 现 将 来 对 该 表 的 访 问。

互 操 作: 对 象 间 的 协 调 工 作

---- 到 目 前 为 止, 我 们 还 没 有 论 述 到 对 象 序 列 化 本 身。 上 面 所 述 仅 仅 是 将 对 象 转 换 成 串( 序 列 化), 然 后 再 把 串 转 换 成 对 象( 反 序 列 化)。 构 件 技 术 与 字 处 理 器 非 常 相 似, 它 有 特 定 的 格 式 和 规 则, 这 些 格 式 和 规 则 可 使 序 列 化 和 非 序 列 化 自 动 实 现, 它 们 在 构 件 技 术 规 范 中 说 明。 在 对 序 列 化 格 式 实 行 标 准 化 后, 因 为 开 发 人 员 可 以 确 切 地 知 道 从 规 范 的 串 中 能 获 得 哪 些 信 息, 所 以 软 件 就 能 够 对 序 列 化 的 数 据 串 进 行 细 微 操 作。

---- 序 列 化 工 作 的 标 准 化 也 使 编 程 更 为 简 单。 程 序 员 完 全 有 可 能 仔 细 检 查 应 用 中 需 要 持 久 的 每 一 个 类, 写 一 些“ 读 这 个, 读 那 个” 和“ 写 这 个, 写 那 个” 的 函 数, 但 是 这 样 工 作 非 常 繁 琐。 幸 好 构 件 技 术 规 范 中 提 供 了 详 细 的 序 列 化 工 作 的 指 导, 因 而 许 多 序 列 化 代 码 是" 免 费 的"。( 这 把 程 序 员 解 脱 开 来 去 完 成 别 的 编 程 任 务。)

---- 把 对 象 序 列 化 工 作 标 准 化 的 一 个 最 大 的 好 处 是 它 允 许 不 同 的 构 件 技 术, 甚 至 是 不 同 的 语 言 来 共 享 和 处 理 彼 此 的 对 象。 例 如, 如 果WordPerfect 能 读 写WordStar 文 件 的 话( 先 不 管 为 什 么), 为 什 么JavaBean 不 能 读 写 一 个 包 含 有 序 列 化 的OLE 或OpenDoc 对 象 呢 ?( 答 案 是 它 能 够-- 至 少 在 理 论 上 是 这 样。) 如 果OpenDoc 的 序 列 化 规 范 能 公 开 的 话, 就 能 写 出 能 读OpenDoc 对 象 的JavaBean。 JavaBean 也 能 按 照OpenDoc 对 象 的 格 式 把 它 自 己 的 内 部 数 据 写 回 文 件。 当 以 后 一 个“ 真 正 的” OpenDoc 应 用 打 开 这 个 文 件 时, 它 会 发 现 新 的 状 态 与 它 自 己 的 格 式 一 致, 而 不 会 想 到JavaBean 与 它 有 什 么 关 系。 只 要 标 准 是 开 放 的( 封 闭 的 标 准 比 没 有 标 准 更 为 糟 糕), 不 同 的 构 件 就 应 该 能 够 互 相 操 作。

分 布 式 系 统

---- 几 年 以 前 我 在Amsterdam 为 了 等 一 个 保 险 单 邮 件 呆 了 一 个 星 期, 不 知 什 么 原 因, 那 份 保 险 单 没 有 传 真 过 来 让 我 签 名。 其 实 在 什 么 地 方 签 名 并 没 有 什 么 关 系, 我 只 需 签 上 我 的 名 字, 然 后 把 它 发 回 以 便 作 进 一 步 处 理。 如 果 我 可 以 选 择 传 真 方 式 来 办 这 件 事 的 话, 只 要 让 我 的 老 爸 把 保 险 单 序 列 化 成 电 脉 冲( 通 过 传 真 机), 在Amsterdam 它 就 能 反 序 列 化 为 一 个 副 本( 用 另 一 台 传 真 机), 我 签 上 名 后, 再 把 它 用 邮 件 或 传 真 寄 回 美 国。

---- 对 象 序 列 化 使 软 件 对 象 在 某 些 方 面 类 似 于 上 面 的 保 险 单 例 子。 在 完 成 某 一 特 定 任 务 时, 可 能 手 头 缺 少 一 些 必 要 资 源( 在 上 面 的 例 子 中, 我 的 手 就 是 资 源) 或 者 是 把 对 象 打 包 并 运 到 别 的 机 器 上 进 行 处 理 会 更 便 宜 些, 这 个 过 程 称 为 负 载 平 衡; 在 另 一 些 情 况 下, 现 存 的( 遗 留 下 来 的) 系 统 可 以 与 新 的 软 件 层 捆 绑, 这 意 味 着 它 们 可 以 作 为 网 络 服 务 被 重 新 打 包。 对 象 能 被 序 列 化 并 送 回 遗 留 系 统 的“ 包 裹” 代 码, 该 代 码 能 重 建 对 象, 用 原 来 的 系 统 上 运 行 这 些 对 象, 并 且 把 它 们 送 回 创 建 它 们 的 系 统( 或 送 到 其 他 系 统) 中。

---- 分 布 式 系 统 可 以 大 致 定 义 为 这 么 一 个 系 统: 该 系 统 中 的 数 据 可 以 由 几 个 处 理 器 中 的 任 意 一 个 来 操 作。 所 有 这 样 的 应 用 都 是 分 布 式 处 理 的 例 子: 既 软 件 对 象 不 一 定 要 在 创 建 它 的 计 算 机 上 运 行。

---- 对 象 请 求 代 理(ORB) 是 分 布 式 处 理 的 一 个 例 子, 它 序 列 化 对 象 和 方 法 调 用 参 数, 并 把 它 们 传 递 到 远 端 系 统 进 行 处 理。 对 象 请 求 代 理 的 常 见 的 例 子 是CORBA。CORBA 如 此 完 整 地 规 定 了 对 象 格 式 和 操 作, 以 至 于 运 行 在 网 络 上 不 同 计 算 机 中 的 程 序 都 能 建 立 和 处 理 对 象, 即 使 这 些 程 序 是 用 不 同 的 语 言 写 的。

JavaBeans 的 序 列 化

---- JavaBeans API 规 范 详 细 说 明 了Bean 序 列 化 机 制 的 目 标 和 如 何 在API 中 实 现 这 些 目 标。JavaBeans 的 持 久 性 建 立 在Java 1.1 的 两 个 特 征 上: 序 列 化( 主 要 特 征) 和 回 顾( 建 立 在 反 映 机 制 上)。

---- java.io 包 中 加 入 了 几 个 类 和 接 口 来 支 持 对 象 序 列 化。 这 些 新 的 类 和 接 口 知 道 如 何 读 写 所 有 的Java 内 置 数 据 类 型, 如byte、 int、 double 型 等 等, 因 此 带 来 了 很 大 的 方 便。( 串 用 通 用 传 输 格 式, 既UTF 写。) 在java.io.DataOutput 和 java.io.DataInput 中 说 明 了 如 何 读 写 这 些 数 据 类 型 的 接 口。 这 些 接 口 在 不 同 的 地 方 执 行, 其 中 包 括java.io.ObjectOutputStream 和 java.io.ObjectInputStream。 对 象 输 入 输 出 流 依 照Java 对 象 序 列 化 规 范 中 的 规 则 来 输 出 对 象 内 容。

---- 为 使java.io 中 的 这 些 新 类 象 规 定 的 那 样 工 作, 通 常 需 要 知 道 一 些 类 文 件 的 有 关 信 息, 这 些 信 息 只 有Java 1.1 的 机 制 才 能 提 供。 反 映 机 制 使 我 们 可 以 用 程 序 描 述 类 的 域 和 方 法。 这 意 味 着 我 们 有 可 能 写 一 个 通 用 的 类, 它 描 绘 了 对 象 是 如 何 连 接 的( 参 照 引 用)、 所 有 的 每 个 对 象 都 包 含 的 域, 并 用 一 步 操 作( 递 归) 自 动 地 把 整 个 状 态 写 到 流 中。 对 许 多Java 类( 最 值 得 注 意 的 是java.awt.Component) 来 说,Sun 公 司 的JavaSoft 小 组 已 经 完 成 了 这 些 工 作。 为 了 把 你 自 己 的 类 放 到ObjectOutputStream 中( 或 从 每 个ObjectInputStream 类 中 建 立 一 个 类), 你 的 类 只 需 实 现java.io.Serializable 和 java.io.Externalizable 这 两 个 接 口 的 其 中 之 一。 后 者 使 与 别 的 构 件 技 术 互 相 操 作 成 为 可 能。 下 面 我 们 集 中 讨 论 可 序 列 化 的 接 口。

---- 示 例: 建 立 一 个 可 序 列 化 的bean

---- 下 面 我 们 来 看 一 个 使bean 序 列 化 的 例 子。 接 口java.io.Serializable 规 定 实 现 它 的 类 要 包 含 两 个 方 法, 这 两 个 方 法 均 要 有 以 下 特 征:

 private void writeObject(java.io.ObjectOutputStream out)
     throws IOException;

 private void readObject(java.io.ObjectInputStream in)
     throws IOException, ClassNotFoundException;
---- 我 们 看 到java.io.Serializable 方 法 选 择 了java.io.ObjectInputStream 或 java.io.ObjectOutputStream 作 为 参 数。 这 些 输 入/ 输 出 流 包 括 了 从 下 一 级 流 读 出 或 写 入 基 本 数 据 类 型 的 实 际 信 息。 如 果 你 以a,b,c 的 顺 序 把 域 写 入ObjectOutputStream , 当 你 以 同 样 的 顺 序 读 出 流 时, 你 会 得 到 同 样 的 对 象( 假 定 你 的 代 码 正 确)。 ObjectInputStream 和 ObjectOutputStream 类 分 别 继 承 了java.io.InputStream 和 java.io.OutputStream 这 两 个 标 准 的 用 于 文 件 和 端 口 的 输 入/ 输 出 流。 这 一 点 很 重 要, 因 为 我 们 可 以 通 过 网 络 用 同 一 个 接 口 把 对 象 存 到 文 件 中 并 发 送 出 去。 对 象 流 要 求 包 含 非 常 详 细 的 信 息, 如 块 对 齐 和 缓 存。( 缓 存 序 列 化 了 的 对 象 流 是 为 了 提 高 性 能, 把 它 的 域 与 块 边 界 对 齐 是 为 了 使 它 更 易 于 作 为 文 本 来 处 理。)

---- 如 果 一 个 类 声 明 能 执 行java.io.Serializabl , 就 意 味 着 该 类 能 把 自 己 写 到 流 中。 幸 运 的 是,Java 序 列 化 很 容 易 做 到 这 一 点: 你 的 代 码 只 需 把 自 己 的 域 作 为writeObject() 域 的 对 象 写 到 流 中, 将 其 读 到read Dbjectf() 域 中, 同 时 要 始 终 保 证 读 写 的 顺 序 一 致。 下 面 这 个SampleObject1 类 就 是 可 序 列 化 对 象 的 一 个 小 小 的 例 子。 这 个 类 只 有 两 个 域, 下 面 这 段 程 序 实 现 了 对 象 序 列 化 本 身 所 必 需 的 一 些 函 数。

import java.io.*;
import java.lang.*;

// this is boring, but it gets the point across.
public class SampleObject1
    extends java.lang.Object
    implements java.io.Serializable {

    protected int a_;
    protected int b_;
    protected String sTitle_ = new String("");

    // Necessary to be a well-behaved Bean.
    public SampleObject1()
    {
    }

    // Create manually.
    public SampleObject1(int a, int b, String s) {
        a_ = a;
        b_ = b;
        sTitle_ = s;
    }

    public void print()
    {
        System.out.println("a=" + a_ + "\nb=" + b_ + "\ns=" + sTitle_);
    }

    // How to write myself to a stream
    private void writeObject(java.io.ObjectOutputStream out)
     throws IOException
    {
        out.writeInt(a_);
        out.writeInt(b_);
        if (sTitle_ == null)
            out.writeObject(null);
        else
            out.writeUTF(sTitle_);
    };

        // How to load myself from a stream
    private void readObject(java.io.ObjectInputStream in)
     throws IOException, ClassNotFoundException
    {
        a_ = in.readInt();
        b_ = in.readInt();
        sTitle_ = in.readUTF();
    }

        // Properties
    public void setTitle(String sTitle) { sTitle_ = sTitle; }
    public String getTitle() { return sTitle_; }

    public void setA(int aa) { a_ = aa; }
    public int getA() { return a_; }

    public void setB(int bb) { b_ = bb; }
    public int getB() { return b_; }

};
---- 上 面 的 类 扩 展 了java.lang.Object , 这 是 因 为 从 流 中 建 立beans 的 函 数 必 须 能 够 获 得 对 象 的 类。java.lang.Object 类 提 供 了 这 种 能 力。 程 序 中 还 向 编 译 器 声 明 了 要 执 行java.io.Serializable 。 代 码 中 内 部 串 被 初 始 化 成 空 串。 这 防 止 了 初 始 串 为null。 如 果 我 们 想 让 它 为null 话,BeanBox 能 忽 略 Title 属 性 而 只 显 示a 和b 的 属 性。 在 该 串 后 面 是 一 个 公 共 无 参 数 构 造 函 数, 所 有 的JavaBeans 都 需 要 用 它 来 建 立 空 对 象。 程 序 最 后 是writeObject() 和 readObject() 的 实 现。 它 们 只 是 简 单 地 从 串 中 读 出 或 写 入 它 们 的 状 态 然 后 返 回。

---- 你 会 发 现, 如 果 你 的 类 已 经 是 某 个 现 存 类( 比 如java.awt.Component) 的 子 类, 你 就 只 需 指 定 你 的 类 去 执 行java.io.Serializable, 对 象 的readObject() 和 writeObject() 方 法 在 你 的 父 类 中 已 经 写 好 了。 这 两 个 方 法 记 录 了 象 初 始 化 超 类 和 追 踪 对 别 的 对 象 的 引 用 这 样 一 些 细 节。 我 们 重 写 readObject() 和 writeObject() 仅 仅 是 为 了 说 明 它 是 如 何 工 作 的。

---- 实 际 上, 你 可 以 注 释 掉readObject() 和writeObject() 函 数, 这 个 类 依 然 能 很 好 地 序 列 化。 为 什 么 我 们 没 必 要 定 义 这 些 函 数 呢 ? 因 为 每 一 个 类 都 是java.lang.Object 的 子 类,java.io.ObjectOutputStream 知 道 如 何 序 列 化 一 个 对 象。 但 是 类 还 必 须 声 明 它 能 实 现 序 列 化, 否 则 的 话ObjectOutputStream 将 会 警 告 发 出NotSerializableException 警 告 。 在 实 际 编 程 中, 只 有 当 你 需 要 扩 充 或 修 改readObject() 和writeObject() 函 数 时 才 重 写 它 们。

---- 下 面 是 一 个 非 常 简 单 的“main” 程 序, 它 能 建 立 包 含 序 列 化 了 的bean 的 文 件。 序 列 化 了 的bean 的 文 件 习 惯 上 有.ser 的 扩 展 名。 如 果 主 程 序 的 第 一 个 参 数 是“w”, 那 么 它 就 按 整 数 读 入 随 后 的 两 个 参 数, 按 字 符 串 读 出 其 余 的 参 数, 用 这 些 值 建 立 一 个SampleObjectf1, 并 将 它 序 列 化 为 一 个.ser 文 件。 如 果 第 一 个 参 数 是“r”, 那 么 它 就 把 下 一 个 参 数 作 为 文 件 名 读 出, 按ObjectInputStream 打 开 那 个 文 件, 调 用ObjectInputStream.readObject() 方 法 从 文 件 中 读 出 对 象, 并 把 结 果 传 递 给SampleObject1, 最 后 命 令 产 生 的 对 象 把 它 自 己 显 示 出 来。

import java.io.*;
import SampleObject1;

public class Demo1 {

    private static void Usage() throws java.io.IOException
    {
        System.out.println("Usage:\n\tDemo1 w file a b\n\tDemo1 r file");

        IOException ex = new IOException("ERROR");
        throw ex;
    }

    public static void main(String[] args)
    {
        String cmd = args[0];

        try {
            if (cmd.compareTo("w") == 0)
            {
                if (args.length != 5)
                {
                    Usage();            // UNIX anyone?
                }

                int aa = Integer.parseInt(args[2]);
                int bb = Integer.parseInt(args[3]);
                String ss = args[4];

                SampleObject1   bar = new SampleObject1(aa, bb, ss);

                FileOutputStream f = new FileOutputStream(args[1]);
                RomanOutStream s = new RomanOutStream(f);

                System.out.println("Write SampleObject1 a=" + aa +
                                   ", b=" + bb +
                                   ", s='" + ss + "'");

                s.writeObject(bar);
                s.flush();
            }

            else if (cmd.compareTo("r") == 0)
            {
                if (args.length != 2)
                {
                    Usage();
                }

                FileInputStream f = new FileInputStream(args[1]);
                ObjectInputStream s = new ObjectInputStream(f);

                System.out.println("Read SampleObject1:");

                SampleObject1 bar = (SampleObject1) s.readObject();
                bar.print();
            }

            else {
                System.err.println("Unknown command " + cmd);
                Usage();
            }
        }

        catch (IOException ex) {
            System.out.println("IO Exception:");
            System.out.println(ex.getMessage());
            ex.printStackTrace();
        }
        catch (ClassNotFoundException ex) {
            System.out.println("ClassNotFound Exception:");
            System.out.println(ex.getMessage());
            ex.printStackTrace();
        }
    }
};

---- 你 可 以 按 下 面 的 步 骤 用 上 述 程 序 来 建 立 一 个 包 含 有 序 列 化bean 的.ser 文 件。

---- C:\> java Demo1 w demo1.ser 10 22 Hallelujah!
---- Write SampleObject1 a=10, b=22, s='Hallelujah!'

---- 建 立 一 个 序 列 化 的bean 本 身 没 那 么 容 易, 但 是 如 果 你 把SampleObject1 类 文 件 和 序 列 化 的 bean 一 起 放 在 一 个JAR 文 件 中, 并 用LoadJar 把 它 装 到BeanBox 中, 会 发 现 已 序 列 化 了 的"Demo1" bean 出 现 在BeanBox 的 工 具 窗 口 中, 并 且 已 经 初 始 化, 可 以 方 便 地 使 用。

---- 装 入JAR 文 件( 用LoadJar), 然 后 把demo1 bean( 序 列 化 了 的SampleObject1 bean) 放 到BeanBox 中。

---- 如 果demo1 是 带 有 值a=10, b=22, Title=Hallelujah! 的SampleObject1 则BeanBox 就 应 显 示 如 下:

图1 装 入BeanBox 中 的 序 列 化 的SampleObject1

---- 上 面 我 们 仅 用 几 行 代 码 就 写 了 一 段 能 把 对 象 作 为 文 件 写 出 来 的 程 序, 别 的 程 序(BeanBox) 能 用 这 个 文 件 在 它 自 己 的 内 存 空 间 中 重 建 对 象。 Demo1 程 序 和 BeanBox 是 可 以 互 相 操 作 的, 尽 管 是 在 非 常 低 级 的 水 平。 你 还 可 以 在BeanBox 中 放 一 个SampleObject1, 用 序 列 化 构 件 函 数 把 它 写 入.ser 文 件, Demo1 ( 带 参 数 "r" 和 文 件 名) 就 能 将 该 对 象 读 出 并 显 示。

---- 到 这 儿 为 止 我 们 说 明 了 应 用 程 序 是 如 何 用 序 列 化 对 象 格 式 来 实 现 互 操 作 的。 当 程 序 运 行 在 网 络 中 不 同 的 机 器 上 时 这 种 操 作 就 变 得 更 有 意 义。

用java.beans.Beans.instantiate() 来 实 例 化beans

---- 在 上 面 例 子 中, 当Demo1 类 中 的 代 码 接 收“r” 参 数 时, 从 文 件 中 读 入 对 象。 它 打 开 文 件, 建 立 流, 进 行 类 型 匹 配 等 等。 有 一 种 方 法 更 为 简 单: 用 静 态 函 数java.beans.Beans.instantiate()。 这 个 函 数 实 例 化JavaBean, 给 定 ClassLoader 和 一 个 字 符 串, 该 字 符 串 是 该 类 的 名 字。

---- instantiate() 函 数 完 成 各 种 各 样 的 任 务: 查 找 类 或 序 列 化 对 象, 按CLASSPATH 搜 索 .ser 文 件, 甚 至 操 纵 Applet 对 象。 现 在, 我 们 用 对instantiate() 的 单 次 调 用 来 取 代Demo1 例 子 中 的 对 象 读 入 代 码:

import java.beans.*;
class Demo2 ... {

        // ... code from previous listing...

        else if (cmd.compareTo("r") == 0)
        {
            if (args.length != 2)
            {
                Usage();
            }

            System.out.println("Read SampleObject1:");

            SampleObject1 bar =
(SampleObject1)Beans.instantiate(null, args[1]
            bar.print();
        }
};

---- 如 果instantiate() 函 数 不 能 实 例 化 对 象 的 话 它 就 发 出java.lang.ClassNotFoundException 警 告, 如 果 有I/O 问 题( 例 如 文 件 不 存 在) 则 发 生java.io.IOException 警 告。

结 论

---- 本 文 阐 述 了 为 什 么 要 对 你 的JavaBeans 进 行 序 列 化 以 及 如 何 去 做。 我 们 讨 论 了 软 件 构 件 序 列 化 的 优 点, 并 且 举 例 说 明 了 如 何 在Java 中 序 列 化 对 象。 以 后 我 们 将 讨 论 对 象 的 序 列 化 结 构, 以 及 如 何 获 得 更 多 的 对bean 序 列 化 格 式 的 控 制。
编 译: 孙 国 泉
来 源:http://www.javaworld.com/javaworld/


中国计算机世界出版服务公司版权所有