Untitled Document

第 一 章 Java语 言 的 产 生 及 其 特 点

1.1 Java产 生 的 历 史 与 现 状

1.1.1 Java产 生 的 历 史

    Java来 自 于Sun公 司 的 一 个 叫Green的 项 目， 其 原 先 的 目 的 是 为 家 用 消 费 电 子 产 品 开 发 一 个 分 布 式 代 码 系 统， 这 样 我 们 可 以 把E-mail发 给 电 冰 箱、 电 视 机 等 家 用 电 器， 对 它 们 进 行 控 制， 和 它 们 进 行 信 息 交 流。 开 始， 准 备 采 用C++,但C++太 复 杂， 安 全 性 差， 最 后 基 于C++开 发 一 种 新 的 语 言Oak(Java的 前 身)，Oak是 一 种 用 于 网 络 的 精 巧 而 安 全 的 语 言，Sun公 司 曾 依 此 投 标 一 个 交 互 式 电 视 项 目， 但 结 果 是 被SGI打 败。 可 怜 的Oak几 乎 无 家 可 归， 恰 巧 这 时Mark Ardreesen开 发 的Mosaic和Netscape启 发 了Oak项 目 组 成 员， 他 们 用Java编 制 了HotJava浏 览 器， 得 到 了Sun公 司 首 席 执 行 官Scott McNealy的 支 持， 触 发 了Java进 军Internet。 Java的 取 名 也 有 一 个 趣 闻， 有 一 天， 几 位Java成 员 组 的 会 员 正 在 讨 论 给 这 个 新 的 语 言 取 什 么 名 字， 当 时 他 们 正 在 咖 啡 馆 喝 着 Java(爪 哇) 咖 啡， 有 一 个 人 灵 机 一 动 说 就 叫Java怎 样， 得 到 了 其 他 人 的 赞 赏， 于 是，Java这 个 名 字 就 这 样 传 开 了。

1.1.2 Java的 现 状

    Java是Sun公 司 推 出 的 新 的 一 代 面 向 对 象 程 序 设 计 语 言， 特 别 适 合 于Internet应 用 程 序 开 发， 它 的 平 台 无 关 性 直 接 威 胁 到Wintel的 垄 断 地 位。 一 时 间，\" 连Internet， 用Java编 程\"， 成 为 技 术 人 员 的 一 种 时 尚。 虽 然 新 闻 界 的 报 导 有 些 言 过 其 实， 但Java作 为 软 件 开 发 的 一 种 革 命 性 的 技 术， 其 地 位 已 被 确 立， 这 表 现 在 以 下 几 个 方 面： 1.计 算 机 产 业 的 许 多 大 公 司 购 买 了 Java的 许 可 证， 包 括IBM,Apple,DEC,Adobe,Silicon Graphics,HP,Oracel,Toshiba， 以 及 最 不 情 愿 的Microsoft。 这 一 点 说 明，Java已 得 到 了 工 业 界 的 认 可。 2.众 多 的 软 件 开 发 商 开 始 支 持Java的 软 件 产 品。 例 如：Borland公 司 正 在 开 发 的 基 于Java的 快 速 应 用 程 序 开 发 环 境Latte， 预 计 产 品 会 在1996年 中 期 发 布。Borland公 司 的 这 一 举 措， 推 动 了Java进 入PC机 软 件 市场。Sun公 司 自 己 的Java开 发 环 境Java Workshop已 经 发 布。 数 据 库 厂 商 如：Illustra,Sysbase,Versant,Oracle都 在 开 发CGI接 口， 支 持HTML和Java。 今 天 是 以 网 络 为 中 心 的 计 算 时 代， 不 支 持HTML和Java， 应 用 程 序 的 应 用 范 围 只 能 限 于 同 质 的 环 境（相 同 的 硬 件 平 台）。 3.Intranet正 在 成 为 企 业 信 息 系 统 最 佳 的 解 决 方 案， 而 其 中Java将 发 挥 不 可 替 代 的 作 用。Intranet的 目 的 是 把Internet用 于 企 业 内 部 的 信 息 系 统， 它 的 优 点 表 现 在： 便 宜， 易 于 使 用 和 管 理。 用 户 不 管 使 用 何 种 类 型 的 机 器 和 操 作 系 统， 界 面 是 统 一 的Intrnet浏 览 器， 而 数 据 库、Web页 面、 应 用 程 序(用Java编 的Applet)则 存 在WWW服 务 器 上， 无 论 是 开 发 人 员， 还 是 管 理 人 员， 抑 或 是 用 户 都 可 以 受 益 于 该 解 决 方 案。 Java语 言 正 在 不 断 发 展 和 完 善，Sun公 司 是 主 要 的 发 展 推 动 者， 较 通 用 的 编 译 环 境 有JDK(Java Develop Kit)与JWS(Java Workshop)。 还 有 很 多 其 他 公 司 正 在 开 发Java语 言 的 编 译 器 与 集 成 环 境， 预 计 不 久Java语 言 的 正 确 性 与 效 率 都 将 会 提 高， 用 户 用Java编 程 和 现 在 用C++编 程 一 样 方 便。

1.2 Java语 言 的 特 点

    Java到 底 是 一 种 什 么 样 的 语 言 呢？ Java是 一 种 简 单 的 面 象 对 象 的 分 布 式 的 解 释 的 健 壮 的 安 全 的 结 构 中 立 的 可 移 植 的 性 能 很 优 异 的 多 线 程 的 动 态 的 语 言。

1.2.1 简 单

    Java最 初 是 为 对 家 用 电 器 进 行 集 成 控 制 而 设 计 的 一 种 语 言， 因 此 它 必 须 简 单 明 了。Java语 言 的 简 单 性 主 要 体 现 在 以 下 三 个 方 面： 1. Java的 风 格 类 似 于C++， 因 而C++程 序 员 是 非 常 熟 悉 的。 从 某 种 意 义 上 讲，Java语 言 是C及C++语 言 的 一 个 变 种， 因 此，C++程 序 员 可 以 很 快 就 掌 握Java编 程 技 术。 2. Java摒 弃 了C++中 容 易 引 发 程 序 错 误 的 地 方， 如 指 针 和 内 存 管 理。 3. Java提 供 了 丰 富 的 类 库。

1.2.2 面 向 对 象

    面 向 对 象 可 以 说 是Java最 重 要 的 特 性。Java语 言 的 设 计 完 全 是 面 向 对 象 的， 它 不 支 持 类 似C语 言 那 样 的 面 向 过 程 的 程 序 设 计 技 术。Java支 持 静 态 和 动 态 风 格 的 代 码 继 承 及 重 用。 单 从 面 向 对 象 的 特 性 来 看，Java类 似 于SmallTalk， 但 其 它 特 性、 尤 其 是 适 用 于 分 布 式 计 算 环 境 的 特 性 远 远 超 越 了SmallTalk。

1.2.3 分 布 式

    Java包 括 一 个 支 持HTTP和FTP等 基 于TCP/IP协 议 的 子 库。 因 此，Java应 用 程 序 可 凭 借URL打 开 并 访 问 网 络 上 的 对 象， 其 访 问 方 式 与 访 问 本 地 文 件 系 统 几 乎 完 全 相 同。 为 分 布 环 境 尤 其 是Internet提?copy;动 态 内 容 无 疑 是 一 项 非 常 宏 伟 的 任 务， 但Java的 语 法 特 性 却 使 我 们 很 容 易 地 实 现 这 项 目 标。

1.2.4 健 壮

    Java致 力 于 检 查 程 序 在 编 译 和 运 行 时 的 错 误。 类 型 检 查 帮 助 检 查 出 许 多 开 发 早 期 出 现 的 错 误。Java自 已 操 纵 内 存 减 少 了 内 存 出 错 的 可 能 性。Java还 实 现 了 真 数 组， 避 免 了 覆 盖 数 据 的 可 能。 这 项 功 能 特 征 大 大 缩 短 了 开 发Java应 用 程 序 的 周 期。 Java提 供Null指 针 检 测 数 组 边 界 检 测 异 常 出 口 字 节 代 码 校 验

1.2.5 结 构 中 立

    另 外， 为 了 建 立Java作 为 网 络 的 一 个 整 体，Java将 它 的 程 序 编 译 成 一 种 结 构 中 立 的 中 间 文 件 格 式。 只 要 有 Java运 行 系 统 的 机 器 都 能 执 行 这 种 中 间 代 码。 现 在，Java运 行 系 统 有Solaris2.4(SPARC),Win32系 统(Windows95和Windows NT)等. Java源 程 序 被 编 译 成 一 种 高 层 次 的 与 机 器 无 关 的byte-code格 式 语 言， 这 种 语 言 被 设 计 在 虚 拟 机 上 运 行， 由 机 器 相 关 的 运 行 调 试 器 实 现 执 行。

1.2.6 安 全

    Java的 安 全 性 可 从 两 个 方 面 得 到 保 证。 一 方 面， 在Java语 言 里， 象 指 针 和 释 放 内 存 等C++功 能 被 删 除， 避 免 了 非 法 内 存 操 作。 另 一 方 面， 当Java用 来 创 建 浏 览 器 时， 语 言 功 能 和 一些浏 览 器 本 身 提 供 的 功 能 结 合 起 来， 使 它 更 安 全。 Java语 言 在 你 的 机 器 上 执 行 前， 要 经 过 很 多 次 的 测 试。 它 经 过 代 码 校 验， 检 查 代 码 段 的 格 式， 检 测 指 针 操 作， 对 象 操 作 是 否 过 分 以 及 试 图 改 变 一 个 对 象 的 类 型。

1.2.6.1 Byte-code校 验

    如 果byte-code通 过 代 码 校 验， 没 有 返 回 错 误， 我 们 可 知 道： 代 码 没 有 堆 栈 上 溢 出 和 下 溢 出 所 有 操 作 代 码 参 数 类 型 都 是 正 确 的 没 有 发 生 非 法 数 据 转 换， 如 将 整 数 转 换 成 指 针。 访 问 对 象 操 作 是 合 法 的

1.2.6.2 类 装 载

    Class Loader通 过 将 本 机 类 与 网 络 资 源 类 的 名 称 分 开， 来 保 持 安 全 性。 因 为 调 入 类 时 总 要 经 过 检 查， 这 样 避 免 了 特 洛 伊 木 马 现 象 的 出 现。 从 网 络 上 下 载 的 类 被 调 进 一 个 与 源 相 关 的 私 有 的 名 字 域。 当 一 个 私 有 类 访 问 另 一 个 类 时，build-in(本 机 类)首 先 被 检 查， 然 后 检 查 相 关 的 类。 这 样 就 避 免 了 破 坏 本 机 类 情 况 的 出 现。

1.2.7 可 移 植 的

    同 体 系 结 构 无 关 的 特 性 使 得Java应 用 程 序 可 以 在 配 备 了Java解 释 器 和 运 行 环 境 的 任 何 计 算 机 系 统 上 运 行， 这 成 为Java应 用 软 件 便 于 移 植 的 良 好 基础。 但 仅 仅 如 此 还 不 够。 如 果 基 本 数 据 类 型 设 计 依 赖 于 具 体 实 现， 也 将 为 程 序 的 移 植 带 来 很 大 不 便。 例 如 在Windows 3.1中 整 数(Integer)为16bits， 在Windows 95中 整 数 为32bits， 在DEC Alpha中 整 数 为64bits， 在Intel 486中 为32bits。 通 过 定 义 独 立 于 平 台 的 基 本 数 据 类 型 及 其 运 算，Java数 据 得 以 在 任 何 硬 件 平 台 上 保 持 一 致。Java语 言 的 基 本 数 据 类 型 及 其 表 示 方 式 如 下： byte 8-bit 二 进 制 补 码 short 16-bit 二 进 制 补 码 int 32-bit 二 进 制 补 码 long 64-bit 二 进 制 补 码 float 32-bit IEEE 754浮点 数 double 32-bit IEEE 754浮点 数 char 16-bit Unicode字 符。

    在 任 何Java解 释 器 中， 数 据 类 型 都 是 依 据 以 上 标 准 具 体 实 现 的。 因 为 几 乎 目 前 使 用 的 所 有CPU都 能 支 持 以 上 数 据 类 型、8～64位 整 数 格 式 的 补 码 运 算 和 单/双 精 度浮点 运 算。 Java编 译 器 本 身 就 是 用Java语 言 编 写 的。Java运 算 系 统 的 编 制 依 据POSIX方 便 移 植 的 限 制， 用ANSI C语 言 写 成。Java语 言 规 范 中 也 没 有 任 何\"同 具 体 实 现 相 关\"的 内 容。

1.2.8 解 释 的

    Java解 释 器(运 行 系 统)能 直 接 运 行 目 标 代 码 指 令。 链 接 程 序 通 常 比 编 译 程 序 所 需 资 源 少， 所 以 程 序 员 可 以 在 创 建 源 程 序 上 花 上 更 多 的 时 间。

1.2.9 高 性 能

    如 果 解 释 器 速 度 不 慢，Java可 以 在 运 行 时 直 接 将 目 标 代 码 翻 译 成 机 器 指 令。Sun用 直 接 解 释 器 一 秒 钟 内 可 调 用300,000个 过 程。 翻 译 目 标 代 码 的 速 度 与C/C++的 性 能 没 什 么 区 别。

1.2.10 多 线 程

Java提?copy;的 多 线 程 功 能 使 得 在 一 个 程 序 里 可 同 时 执 行 多 个 小 任 务。 线 程 － － 有 时 也 称 小 进 程 － － 是 一 个 大 进 程 里 分 出 来 的 小 的 独 立 的 进 程。 因 为Java实 现 的 多 线 程 技 术， 所 以 比C和C++更 键 壮。 多 线 程 带 来 的 更 大 的 好 处 是 更 好 的 交 互 性 能 和 实 时 控 制 性 能。 当 然 实 时 控 制 性 能 还 取 决 于 系 统 本 身(UNIX,Windows,Macintosh等)， 在 开 发 难 易 程 度 和 性 能 上 都 比 单 线 程 要 好。 任 何 用 过 当 前 浏 览 器 的 人， 都 感 觉 为 调 一 副 图 片 而 等 待 是 一 件 很 烦 恼 的 事 情。 在Java里， 你 可 用 一 个 单 线 程 来 调 一 副 图 片， 而 你 可 以 访 问HTML里 的 其 它 信 息 而 不 必 等 它。

1.2.11 动 态

    Java的 动 态 特 性 是 其 面 向 对 象 设 计 方 法 的 发 展。 它 允 许 程 序 动 态 地 装 入 运 行 过 程 中 所 需 要 的 类， 这 是C++语 言 进 行 面 向 对 象 程 序 设 计 所 无 法 实 现 的。 在C++程 序 设 计 过 程 中， 每 当 在 类 中 增 加 一 个 实 例 变 量 或 一 种 成 员 函 数 后， 引 用 该 类 的 所 有 子 类 都 必 须 重 新 编 译， 否 则 将 导 致 程 序 崩 溃。Java从 如 下 几 方 面 采 取 措施来 解 决 这 个 问 题。Java编 译 器 不 是 将 对 实 例 变 量 和 成 员 函 数 的 引 用 编 译 为 数 值 引 用， 而 是 将 符 号 引 用 信 息 在 字 节 码 中 保 存 下 传 递 给 解 释 器， 再 由 解 释 器 在 完 成 动 态 连 接 类 后， 将 符 号 引 用 信 息 转 换 为 数 值 偏 移 量。 这 样， 一 个 在 存 储 器 生 成 的 对 象 不 在 编 译 过 程 中 决 定， 而 是 延 迟 到 运 行 时 由 解 释 器 确 定 的。 这 样， 对 类 中 的 变 量 和 方 法 进 行 更 新 时 就 不 至 于 影 响 现 存 的 代 码。 解 释 执 行 字 节 码 时， 这 种 符 号 信 息 的 查 找 和 转 换 过 程 仅 在 一 个 新 的 名 字 出 现 时 才 进 行 一 次， 随 后 代 码 便 可 以 全 速 执 行。 在 运 行 时 确 定 引 用 的 好 处 是 可 以 使 用 已 被 更 新 的 类， 而 不 必 担 心 会 影 响 原 有 的 代 码。 如 果 程 序 连 接 了 网 络 中 另 一 系 统 中 的 某 一 类， 该 类 的 所 有 者 也 可 以 自 由 地 对 该 类 进 行 更 新， 而 不 会 使 任 何 引 用 该 类 的 程 序 崩 溃。 Java还 简 化 了 使 用 一 个 升 级 的 或 全 新 的 协 议 的 方 法。 如 果 你 的 系 统 运 行Java程 序 时 遇 到 了 不 知 怎 样 处 理 的 程 序， 没 关 系，Java能 自 动 下 载 你 所 需 要 的 功 能 程 序。

1.3 与C和C++语 言 的 异 同

    Java提 供 了 一 个 功 能 强 大 语 言 的 所 有 功 能， 但 几 乎 没 有 一 点 含 混 特 征。C++安 全 性 不 好， 但C和C++还 是 被 大 家 所 接 受， 所 以Java设 计 成C++形 式， 让 大 家 很 容 易 学 习。Java去 掉 了C++语 言 的 许 多 功 能， 让Java的 语 言 功 能 很 精 炼， 并 增 加 了 一些很 有 用 的 功 能， Java去 掉 了 以 下 几 个C和C++功 能 和 特 征： 指 针 运 算 结 构 typedefs #define 需 要 释 放 内 存 全 局 变 量 的 定 义 这个功 能 都 是 很 容 易 引 起 错 误 的 地 方。

1.4 Java的 应 用 简 介

1.4.1 Web浏 览

    Web浏 览 是 现 在 国 际 网 甚 至 局 域 网 的 主 要 使 用 方 式。 文 档 能 很 容 易 地 显 示 文 本 和 各 种 图 片， 他 还 能 提 供 超 文 本 链 接。 这 些 浏 览 器 调 用HTML语 言 写 的 文 档，HTML/WWW浏 览 器 技 术 只 限 于 文 本 和 图 象。 如 果 你 想 播 放 一 种 声 音 或 运 行 一 个 演 示 程 序， 你 不 得 不 下 载 那 个 文 件 并 用 你 本 机 上 的 能 理 解 和 运 行 那 个 文 件 格 式 的 程 序 来 播 放 它。 Java程 序 和 它 的 浏 览 器HotJava， 提 供 了 可 让 你 的 浏 览 器 运 行 程 序 的 方 法。 你 能 从 你 的 浏 览 器 里 直 接 播 放 声 音。 你 还 能 播 放 页 面 里 的 动 画。Java还 能 告 诉 你 的 浏 览 器 怎 样 处 理 新 的 类 型 文 件。 当 我 们 能 在2400 baud线 上 传 输 视 频 图 象 时，HotJava将 能 显 示 这 些 视 频。

1.4.2 网 络 应 用 系 统

    Java是 一 种 与 平 台 无 关 的 语 言， 因 此 用Java开 发 的 网 络 应 用 系 统 可 以 在 各 种 平 台 上 运 行， 大 大 增 加 了 开 发 效 率， 减 少 重 复 劳 动。 而 且，Java集 成 的 网 络 功 能 分 有 利 于 开 发 网 络 应 用 系 统。

本 章 小 结：
    1.Java的 产 生 与 流 行 是 当 今internet发 展 的 客 观 要 求。
    2.java是 一 门 各 方 面 性 能 都 很 好 的 编 程 语 言， 它 的 基 本 特 点 是 简 单、 面 象 对 象、 分 布 式、 解 释 的、健 壮 的、 安 全 的、 结 构 中 立 的、 可 移 植 的、 性 能 很 优 异 的、 多 线 程 的、 动 态 的。分 适 合 在internet环 境 上 开 发 应 用 系 统。
    3.java可 以 制 作 大 部 分 网 络 应 用 程 序 系 统， 而 且 与 当 今 流 行 的WWW浏 览 器 结 合 得 很 好。


第 二 章 Java程 序 开 发 与 运 行 环 境

2.1 JDK环 境

    Java不 仅 提供了 一 个 丰 富 的 语 言 和 运 行 环 境， 而 且 还 提供了 一 个 免 费 的Java开 发 工 具 集(Java Developers Kits， 简 称JDK)。 编 程 人 员 和 最 终 用 户 可 以 利 用 这 些 工 具 来 开 发java程 序 或 调 用Java 内 容。JDK包 括 以 下 工 具： javac Java语 言 编 译 器， 输 出 结 果 为Java字 节 码 java Java字 节 码 解 释 器 javap Disassembeler: Java字 节 码 分 解 程 序， 本 程 序 返 回Java程 序 的 成 员 变 量 及 方 法 等 信 息。 javaprof 资 源 分 析 工 具， 用 于 分 析Java程 序 在 运 行 过 程 中 调 用 了 哪 些 资 源， 包 括 类 和 方 法 的 调 用 次 数 和 时 间， 以 及 各 数 据 类 型 的 内 存 使 用 情 况 等。 javah C代 码 处 理 工 具， 用 于 从Java类 调 用C++代 码 java Applet Viewer 小 应 用 程 序 浏 览 工 具， 用 于 测 试 并 运 行Java小 应 用 程 序 java Debugger API Java调 试 工 具API Prototype Debugger Java调 试 工 具 原 型 Java开 发 环 境 还 包 括Java类 库(包 括I/O类 库、 用 户 界 面 类 库、 网 络 类 库 等)和HotJava WWW 浏 览 器。 其 中，HotJava浏 览 器 提 供 了 在WWW环 境 下 运 行Java代 码 的 一 个 运 行 系 统， 而 且 还 为WWW开 发 人 员 提 供 了 一 个Java开 发 框 架。Java解 释 器 是 面 向 Java程 序 的 一 个 独 立 运 行 系 统， 它 可 以 一 种 稳 定、 高 性 能 方 式 运 行 那 些 独 立 于 平 台 的Java字 节 码，Java编 译 器 则 用 于 生 成 这 些 字 节 码。

2.1.1 Java程 序 的 编 译

    Java程 序 的 编 译 程 序 是javac.exe。javac命 令 将Java程 序 编 译 成 字 节 码， 然 后 你 可 用java解 释 器java命 令 来 解 释 执 行 这 些Java字 节 码。Java程 序 源 码 必 须 存 放 在 后 缀 为.java的 文 件 里。Java 程 序 里 的 每 一 个 类，javac都 将 生 成 与 类 相 同 名 称 但 后 缀 为.class文 件。 编 译 器 把.class文 件 放 在.java文 件 的 同 一 个 目 录 里， 除 非 你 用 了-d选 项。 当 你 引 用 到 某些自 己 定 义 的 类 时， 必 须 指 明 它 们 的 存 放 目 录， 这 就 需 要 利 用 环 境 变 量 参 数 CLASSPATH。 环 境 变 量CLASSPATH是 由 一 些 被 分 号 隔 开 的 路 径 名 组 成。 如 果 传 递 给javac编 译 器 的 源 文 件 里 引 用 到 的 类 定 义 在 本 文 件 和 传 递 的 其 它 文 件 中 找 不 到， 则 编 译 器 会 按 CLASSPATH定 义 的 路 径 来 搜 索。 例 如： CLASSPATH = .;C:\\java\\classes 则 编 译 器 先 搜 索 当 前 目 录， 如 果 没 搜 索 到， 则 继 续 搜 索C:\\java\\classes目 录。 注 意， 系 统 总 是 将 系 统 类 的 目 录 缺 省 地 加 在CLASSPATH后 面， 除 非 你 用 －classpath选 项 来 编 译。 javac_g 是 一 个 用 于 调 试 的 未 优 化 的 编 译 器， 功 能 与 用 法 和javac一 样。 javac的 用 法 如 下： javac [-g][-O][-debug][-depend][-nowarn][-verbose][-classpath path][-nowrite][-d dir] file.java... 以 下 是 每 个 选 项 的 解 释。

选 项 解 释：

    -classpath path 定 义javac搜 索 类 的 路 径。 它 将 覆 盖 缺 省 的CLASSPATH环 境 变 量 的 设 置。 路 径 是 由 一 些 由 逗 号 隔 开 的 路 径 名 组 成， 一 般 格 式 如 下： .; 例 如： .;C:\\java\\doc\\classes;C:\\tools\\java\\classes 表 示 编 译 器 遇 到 一 个 新 类， 它 先 在 本 文 件 中 查 找 它 的 定 义， 如 果 没 有， 则 在 本 文 件 所 处 目 录 下 其 它 文 件 中 查 找 它 的 定 义， 如 果 还 没 有， 则 继 续 搜 索C:\\java\\doc\\classes目 录 中 的 所 有 文 件， 以 此 类 推。

    -d directory 指 明 类 层 次 的 根 目 录， 格 式 如 下：javac -d MyProgram.java 这 样 将MyProgram.java程 序 里 的 生 产 的.class文 件 存 放 在my_dir目 录 里.

    -g 带 调 试 信 息 编 译， 调 试 信 息 包 括 行 号 与 使 用java调 试 工 具 时 用 到 的 局 部 变 量 信 息。 如 果 编 译 没 有 加 上-O优 化 选 项， 只 包 含 行 号 信 息。

    -nowarn 关 闭 警 告 信 息， 编 译 器 将 不 显 示 任 何 警 告 信 息。

    -O 优 化 编 译static，final，private函 数， 注 意 你 的 类 文 件 可 能 更 大。
    -verbose 让 编 译 器 与 解 释 器 显 示 被 编 译 的 源 文 件 名 和 被 加 载 的 类 名。

环 境 变 量

    CLASSPATH 用 来 提?copy;给 系 统 搜 索 用 户 定 义 的 类 的 缺 省 路 径。 各 路 径 由 分 号 隔 开， 例 如：.;C:\\java\\doc\\classes;C:\\tools\\java\\classes 表 示 编 译 器 遇 到 一 个 新 类， 它 先 在 本 文 件 中 查 找 它 的 定 义， 如 果 没 有， 则 在 本 文 件 所 处 目 录 下 其 它 文 件 中 查 找 它 的 定 义， 如 果 还 没 有， 则 继 续 搜 索C:\\java\\doc\\classes目 录 中 的 所 有 文 件， 以 此 类 推。

2.1.2 Java程 序 的 调 试 使 用java调 试 器

    jdb导 游 在 早 期 前Betal版 的Java调 试 器jdb是 命 令 行 形 式 的， 如 用Sun公 司 的dbx调 试 器。 用jdb来 调 试Java应 用 程 序， 在 调 试 前， 要 确 证 你 的 应 用 程 序 是 带 标 志 －g编 译 的。 例 如： javac -g HelloWorld.java help命 令 将 显 示jdb 里 的 可 用 命 令 列 表。

    >help <命 令 列 表> threads [threadgroup]--列 出 线 程 thread － － 设 置 缺 省 线 程 Suspend [threads id(s)]－ － 将 线 程 挂 起 resume [thread id(s)]－ － 重 新 启 动 线 程 where [id] |a1|－ － 打 印 线 程 的 堆 栈 threadgroups--列 出 线 程 组 号 threadgroup －－ 设 置 当 前 线 程 组 print [id(s)]－ － 打 印 对 象 或 域 dump [id(s)]－ － 打 印 所 有 对 象 信 息 locals－ － 打 印 当 前 堆 栈 所 有 局 部 变 量 classes－ － 列 出 当 前 所 知 的 类 methods － － 列 出 一 个 类 的 成 员 函 数 stop in .－ － 在 一 个 成 员 函 数 里 设 置 断 点 stop at :－ － 在 一 行 里 设 置 断 点 up [n frames]－ － 在 线 程 堆 栈 里 往 上 移 down [n frames]－ － 在 线 程 堆 栈 里 往 下 移 clear :－ － 清 除 一 个 断 点 step － － 执 行 当 前 行 cont－ － 从 断 点 处 继 续 执 行 catch － － 为 指 定 的 情 况 中 断 ignor － － 为 指 定 的 情 况 忽 略 list [line number]－ － 打 印 源 程 序 use [Sourcefile path]－ － 显 示 或 改 变 源 路 径 memeory－ － 报 告 内 存 使 用 情 况 load classname－ － 加 载Java类 以 便 调 试 run － － 开 始 执 行 加 载 的 类 !!－ － 重 复 以 上 的 命 令 help(?)－ － 列 出 所 有 的 命 令 exit( or quit)－ － 离 开 调 试 器

2.1.3 Java程 序 的 执 行

    java - java语 言 解 释 器 java命 令 解 释java 字 节 码 语 法： java [ options ] classname java_g [ options ] classname

    描 述： java命 令 由java编 译 器javac输 出 的Java字 节 码。 classname参 数 是 要 执 行 的 类 名 称。 注 意 任 意 在 类 名 称 后 的 参 数 都 将 传 递 给 要 执 行 类 的main 函 数。 java执 行 完main函 数 后 推 出， 除 非main函 数 创 建 了 一 个 或 多 个 线 程。 如 果main函 数 创 建 了 其 它 线 程，java总 是 等 到 最 后 一 个 线 程 推 出 才 推 出。

选 项：

    -cs, -checksource 当 一 个 编 译 过 的 类 调 入 时， 这 个 选 项 将 比 较 字 节 码 更 改 时 间 与 源 文 件 更 改 时 间， 如 果 源 文 件 更 改 时 间 靠 后， 则 重 新 编 译 此 类 并 调 入 此 新 类。
    -classpath path 定 义javac搜 索 类 的 路 径。 它 将 覆 盖 缺 省 的CLASSPATH环 境 变 量 的 设 置。 路 径 是 由 一?copy;由 逗 号 隔 开 的 路 径 名 组 成， 一 般 格 式 如 下： .; 例 如：.;C:\\java\\doc\\classes;C:\\tools\\java\\classes 表 示 解 释 器 遇 到 一 个 新 类， 它 先 在 本 文 件 中 查 找 它 的 定 义， 如 果 没 有， 则 在 本 文 件 所 处 目 录 下 其 它 文 件 中 查 找 它 的 定 义， 如 果 还 没 有， 则 继 续 搜 索C:\\java\\doc\\classes目 录 中 的 所 有 文 件， 以 此 类 推。

    -mx x 设 置 最 大 内 存 分 配 池， 大 小 为x，x必 须 大 于1000bytes。 缺 省 为16兆。

    -ms x 设 置 垃 圾 回 收 堆 的 大 小 为x，x必 须 大 于1000bytes。 缺 省 为1兆。

    -noasyncgc 关 闭 异 步 垃 圾 回 收 功 能。 此 选 项 打 开 后， 除 非 显 式 调 用 或 程 序 内 存 溢 出， 垃 圾 内 存 都 不 回 收。 本 选 项 不 打 开 时， 垃 圾 回 收 线 程 与 其 它 线 程 异 步 同 时 执 行。

    -ss x 每 个Java线 程 有 两 个 堆 栈， 一 个 是java代 码 堆 栈， 一 个 是C代 码 堆 栈。-ss选 项 将 线 程 理C代 码 用 的 堆 栈 设 置 成 最 大 为x。

    -oss x 每 个Java线 程 有 两 个 堆 栈， 一 个 是java代 码 堆 栈， 一 个 是C代 码 堆 栈。-oss选 项 将 线 程 理java代 码 用 的 堆 栈 设 置 成 最 大 为x。

    -v, -verbose 让java解 释 器 在 每 一 个 类 被 调 入 时， 在 标 准 输 出 打 印 相 应 信 息。
环 境 变 量

    CLASSPATH 用 来 提?copy;给 系 统 搜 索 用 户 定 义 的 类 的 缺 省 路 径。 各 路 径 由 分 号 隔 开， 例 如： .;C:\\java\\doc\\classes;C:\\tools\\java\\classes 表 示 解 释 器 遇 到 一 个 新 类， 它 先 在 本 文 件 中 查 找 它 的 定 义， 如 果 没 有， 则 在 本 文 件 所 处 目 录 下 其 它 文 件 中 查 找 它 的 定 义， 如 果 还 没 有， 则 继 续 搜 索C:\\java\\doc\\classes目 录 中 的 所 有 文 件， 以 此 类 推。

2.2 JWS环 境

    Java WorkShop是SUN公 司 的 一 个 新 产 品,它 是 一 个 集 成 的java语 言 开 发 环 境， 它 包 括 以 下 工 具： l Portfolio 和Project管 理 器 l 源 文 件 编 辑 器 l Build管 理 工 具 l 调 试 器 l 项 目 测 试 l ?copy;展 在 线 超 文 本 连 接 到 帮 助 文 件 这?copy;工 具 在Java WorkShop的 第 一 页 都 有 类 似Web页 面 的 超 级 连 接， 如 图： 注 意，Java WorkShop采 用 的 是 当 今 浏 览 器 的 界 面 风 格， 你 想 作 什 么， 只 需 找 到 相 应 的 超 级 连 接 就 可 以 了， 具 体 编 译 及 调 试 功 能 是 用 嵌 如 到HTML文 档 里 的Applet实 现 的， 因 此， 对 习 惯 于 用internet浏 览 方 式 的 用 户 来 说， 这 种 界 面 很 容 易 接 受。

2.2.1 Portfolio 和Project管 理 器

    Protfolios是 一些java应 用 程 序 或Applet的 集 中。 它 让 你 更 好 的 管 理 更 多 的projects。 一 个project是portfolio里 的 一 个 子 集， 它 包 含 了 以 下 信 息： 1. 怎 样 编 译 本 项 目 2. 怎 样 调 试 和 浏 览 本 项 目 3. 怎 样 运 行 本 项 目 4. 怎 样 发 布 本 项 目

2.2.2 源 文 件 编 辑 器

    源 文 件 编 辑 器 可 以 从build管 理 器、 调 试 器 和 源 文 件 浏 览 器 里 的 超 级 连 接 进 入。 在 这 个 模 块 里， 你 可 以 输 入 源 文 件。

2.2.3 Build管 理 工 具

    本 模 块 是 项 目 的 编 译 器， 你 可 以 点build按 钮 直 接 进 入 本 模 块， 如 果 某 个 文 件 出 错， 错 误 信 息 会 提 供 一 个 超 级 连 接， 直 接 指 到 出 错 的 源 文 件 地 点。

2.2.4 Visul Java（图 形 界 面 构 造）

    顾 名 思 义， 本 模 块 能 让 你 可 视 化 建 造 一个复 杂 界 面， 如 果 你 用 过Visual Basic， 你 会 发 现 它 们 很 相 似。

2.2.5 调 试 器

    调 试 器 能 让 你 很 方 便 地 跟 踪 程 序 的 执 行 与 发 现 程 序 的 错 误。

本 章 小 结：

    Java语 言 有 两 个 开 发 环 境， 一 个 是 免 费 的JDK， 是 命 令 行 方 式 的。 还 有 一 个Java WorkShop， 是 开 发java程 序 的 集 成 环 境。 本 章 简 要 介 绍 了 它 们 的 使 用 方 法。


第 三 章 Java程 序 设 计 基 础

3.1 Java编 程 概 况

    现 在 你 可 以 复 习 一 下Java语 言 的 背 景 材 料， 它 的 基 本 结 构 象C/C++， 但 任 何 用 面 向 过 程 语 言 编 写 过 程 序 的 人 都 可 以 了 解Java语 言 的 大 部 分 结 构.

3.1.1 程 序 结 构

    Java语 言 的 源 程 序 代 码 由 一 个 或 多 个 编 译 单 元(compilation unit)组 成， 每 个 编 译 单 元 只 能 包 含 下 列 内 容(空 格 和 注 释 除 外)： * 一 个 程 序 包 语 句(package statement ) * 入 口 语 句(import statements) * 类 的 声 明(class declarations) * 界 面 声 明(interface declarations) 每 个Java的 编 译 单 元 可 包 含 多 个 类 或 界 面， 但 是 每 个 编 译 单 元 最 多 只 能 有 一 个 类 或 者 界 面 是 公 共 的。 Java 的 源 程 序 代 码 被 编 译 ?reg; 后， 便 产 生 了Java字 节 代 码。Java的 字 节 代 码 由 一 ?copy; 不 依 赖 于 机 器 的 指 令 组 成， 这 ?copy; 指 令 能 被Java的 运 行 系 统(runtime system)有 效 地 解 释。Java的 运 行 系 统 工 作 起 来 如 同 一 台 虚 拟 机。 在 当 前 的Java实 现 中， 每 个 编 译 单 元 就 是 一 个 以.java为 后 缀 的 文 件。 每 个 编 译 单 元 有 若 干 个 类， 编 译 后， 每 个 类 生 成 一 个.class文 件。.class文 件 是Java虚 拟 机 能 够 识 别 的 代 码。

3.1.2 注 释

    三 种 类 型 如 下: //注 释 一 行 /*一 行 或 多 行 注 释*/ /**文 档 注 释**/ 文 档 注 释 一 般 放 在 一 个 变 量 或 函 数 定 义 ?reg; 前,指 示 在 任 何 自 动 生 成 文 档 系 统 中 调 入。 这 ?copy; 注 释 都 是 声 明 条 目 的 描 述.。

3.1.3 标 识 符

    变 量， 函 数， 类 和 对 象 的 名 称 都 是 标 识 符， 程 序 员 需 要 标 识 和 使 用 的 东 西 都 需 要 标 识 符。 在Java语 言 里,标 识 符 以 字 符 或_,$开 头,后 面 可 以 包 含 数 字， 标 识 符 是 大 小 写 有 区 别 的,没 有 长 度 限 制。

    有 效 的 标 识 符 myname ict_network Hello _sys_path $bill

    例 子： int a_number; char _onechar; float $bill;

    关 键 词 abstract continue for new switch boolean default goto null synchronized break do if package this byte double implements private threadsafe byvalue else import protected throw case extends instanceof public transient catch false int return true char final interface short try class finally long static void const float native super while 其 它 保 留 词 以 下 单 词 被 保 留 使 用： cast future generic inner operator outer rest var

3.1.4 数 据 类 型

    Java使 用 五 种 基 本 类 型:integer(整 数),floating(浮点 数),point(指 针),Boolean(布 尔 变 量),Character or String(字 符 或 字 符 串)。 integer 整 数 下 边 给 出 的 数 据 表 示 都 是 整 数 的 例 子： 4 ， 15 ， 089 ， 0xAD00 整 数 长 度 数 据 类 型 表 示 8 bits byte 16 bits short 32 bits int 64 bits long

    floating 浮点 数 下 边 给 出 的 数 据 表 示 都 是浮点 数 的 例 子： 6.37 ， 3.7E15 ， 3e8

    浮点 数 长 度 数 据 类 型 表 示 32 bits float 64 bits double

    Boolean 布 尔 变 量 下 边 是 布 尔 变 量 的 两 种 可 能 取 值： true false Character 字 符 下 边 给 出 的 都 是 字 符 的 例 子： a \\t (tab) \\u????(unicode)

    String 字 符 串 下 边 给 出 的 都 是 字 符 串 的 例 子： \"This is a string literal\" \"中 国 科 学 院 计 算 所\" 数 组 你 可 以 定 义 任 意 类 型 的 数 组. char s[]; 这 是 字 符 型 数 组； int [] array; 这 是 整 型 数 组； 你 还 可 以 定 义 数 组 的 数 组. int block[][]=new int [2][3]; 数 组 边 界 在 运 行 时 被 检 测,避 免 堆 栈 溢 出 和 内 存 崩 溃.

    在Java里,数 组 实 际 上 是 一 个 对 象,数 组 有 一 个 成 员 变 量：length。 你 可 以 用 这 个 成 员 函 数 来 查 看 任 意 数 组 的 长 度. int a[][] = new int [10][3] a.length /* 10 */ a[0].length /* 3 */ 创 建 数 组 在Java里 创 建 数 组,你 可 使 用 两 种 基 本 方 法 ： 一。 创 建 一 个 空 数 组： int list[]=new int[50]; 或 你 可 以 用 初 始 数 值 填 充 数 组. String names[] = { \"Chenji\",\"Yuan\",\"Chun\",\"Yang\" }; 相 当 于 下 面 功 能: String names[]; names = new String[4]; names[0]=new String(\"Chenji\"); names[1]=new String(\"Yuan\"); names[2]=new String(\"Chun\"); names[3]=new String(\"Yang\"); 在 编 译 时 你 不 能 象 下 例 那 样 创 建 静 态 数 组。 int name[50];//将 产 生 一 个 编 译 错 误 你 也 不 能 用new操 作 去 填 充 一 个 没 定 义 大 小 的 数 组。 int name[]; for (int i=0;i<9;i++) { name[i] = i; }

3.1.5 表 达 式

    Java语 言 的 表 达 式 和C语 言 非 常 类 似。 运 算 符 运 算 符(operator)优 先 级 从 高 到 底 排 列 如 下： . [ ] () ++ -- ! ～ instanceof * / % + - << >> >>> < > <= >\\ == ! = & ^ && || ? :
= op = , 整 数 运 算 符 在 整 数 运 算 时， 如 果 操 作 数 是long类 型， 则 运 算 结 果 是long类 型， 否 则 为int类 型， 绝 不 会 是byte，short或char型。 这 样， 如 果 变 量i被 声 明 为short或byte，i+1的 结 果 会 是int。 如 果 结 果 超 过 该 类 型 的 取 值 范 围， 则 按 该 类 型 的 最 大 值 取 模。 单 目 整 数 运 算 符 是： 运 算 符 操 作 - 单 目 非 ～ 位 补 码 ++ 加1 -- 减1 ++运 算 符 用 于 表 示 直 接 加1操 作。 增 量 操 作 也 可 以 用 加 运 算 符 和 赋 值 操 作 间 接 完 成。++ lvalue （左 值?copy; 表 示lvalue+=1, ++lvalue 也 表 示lvalue =lvalue +1 (只 要lvalue没 有 副 作 用)。--运 算 符 用 于 表 示 减1操 作。++和--运 算 符 既 可 以 作 为 前 缀 运 算 符， 也 可 以 做 为 后 缀 运 算 符。 双 目 整 数 运 算 符 是： 运 算 符 操 作** + 加 - 减 * 乘 / 除 % 取 模 & 位 与 ｜ 位 或 ^ 位 异 或 << 左 移 >> 右 移(带 符 号) >>> 添 零 右 移 整 数 除 法 按 零 舍 入。 除 法 和 取 模 遵 守 以 下 等 式： ( a/b ) * b + ( a%b ) == a 整 数 算 术 运 算 的 异 常 是 由 于 除 零 或 按 零 取 模 造 成 的。 它 将 引 发 一 个 算 术 异 常。 下 溢 产 生 零， 上 溢 导 致 越 界。 例 如： 加1超 过 整 数 最 大 值， 取 模 后， 变 成 最 小 值。 一 个op=赋 值 运 算 符， 和 上 表 中 的 各 双 目 整 数 运 算 符 联 用， 构 成 一 个 表 达 式。 整 数 关 系 运 算 符<， >，<=，>=，==和!=产 生boolean类 型 的 数 据。

    布 尔 运 算 符 布 尔(boolean)变 量 或 表 达 式 的 组 合 运 算 可 以 产 生 新 的boolean值。 单 目 运 算 符!是 布 尔 非。 双 目 运 算 符 ＆， ｜ 和^是 逻 辑AND，OR和XOR运 算 符， 它 们 强 制 两 个 操 作 数 求 布 尔 值。 为 避 免 右 侧 操 作 数 冗 余 求 值， 用 户 可 以 使 用 短 路 求 值 运 算 符 ＆ ＆ 和 ｜ ｜。 用 户 可 以 使 用==和!=， 赋 值 运 算 符 也 可 以 用 &=、|=、^=。 三 元 条 件 操 作 符? : 和C语 言 中 的 一 样。

    浮点 运 算 符 浮点 运 算 符 可 以 使 用 常 规 运 算 符 的 组 合： 如 单 目 运 算 符++、--， 双 目 运 算 符+、-、* 和/， 以 及 赋 值 运 算 符+=，-=，*=， 和/=。 此 外， 还 有 取 模 运 算： %和%=也 可 以 作 用 于浮点 数， 例 如： a%b和a-((int) (a/b)*b)的 语 义 相 同。 这 表 示a%b的 结 果 是 除 完 后 剩 下 的浮点 数 部 分。 只 有 单 精 度 操 作 数 的浮点 表 达 式 按 照 单 精 度 运 算 求 值， 产 生 单 精 度 结 果。 如 果浮点 表 达 式 中 含 有 一 个 或 一 个 以 上 的 双 精 度 操 作 数， 则 按 双 精 度 运 算， 结 果 是 双 精 度浮点 数。

    数 组 运 算 符 数 组 运 算 符 形 式 如 下： [ ] 可 给 出 数 组 中 某 个 元 素 的 值。 合 法 的 取 值 范 围 是 从0到 数 组 的 长 度 减1。 取 值 范 围 的 检 查 只 在 运 行 时 刻 实 。 运 算 符 以String对 象 实 现。 运 算 符\"+\"完 成 并 操 作， 如 果 必 要 则 自 动 把 操 作 数 转 换 为String型。 如 果 操 作 数 是 一 个 对 象， 它 可 定 义 一 个 方 法toString ( ) 返 回 该 对 象 的String方 式， 例 如 float a = 1.0 print (\"The value of a is\"+ a +\"\\n\"); +运 算 符 用 到 ?reg; 上 的 例 子 String s=\"a=\"+ a; +=运 算 符 也 可 以 用 于String。 注 意， 左 边(下 例 中 的s1)仅 求 值 一 次。 s1+=a; //s1=s1+a //若a非String型， 自 动 转 换 为String型。 对 象 运 算 符 双 目 运 算 符instanceof 测 试 某 个 对 象 是 否 是 指 定 类 或 其 子 类 的 实 例。 例 如： if(myObject instanceof MyClass) { MyClass anothermyObject=( MyClass) myObject; … } 是 判 定myObject是 否 是MyClass的 实 例 或 是 其 子 类 的 实 例。 强 制 和 转 换 Java语 言 和 解 释 器 限 制 使 用 强 制 和 转 换， 以 防 止 出 错 导 致 系 统 崩 溃。 整 数 和浮点 数 之 间 可 以 来 回
强 制 转 换， 但 整 数 不 能 强 制 转 换 成 数 组 或 对 象。 对 象 不 能 被 强 制 为 基 本 类 型。

3.1.6 Java流 控 制

    下 面 几 个 控 制 结 构 是 从C语 言 借 鉴 的。 分 支 结 构

    if/else分 支 结 构 if (Boolean) { statemanets; } else { statements; }

    switch分 支 结 构 switch(expr1) { case expr2: statements; break; case expr3: statements; break; default: statements; break; }

    循 环 结 构 for循 环 结 构 for (init expr1;test expr2;increment expr3) { statements; }

    While循 环 结 构 While(Boolean) { statements; }

    Do循 环 结 构 do { statements; } while (Boolean);

    一 般 顺 序 控 制 break [label] continue [label] reutrn expr; label:statement;

    for循 环 例 子 下 面 是 一 个 程 序 例 子， 画 几 条 线， 分 别 用 红,绿,蓝 颜 色， 这 段 程 序 可 能 是Java函 数 的 一 部 分： int count; for (count=1;count<=12;count++) { switch (count % 3) } case 0: setColor(Color.red); break; case 1: setColor(Color.blue); break; case 2: setColor(Color.green); break; } g.drawLine(10,count*10,80,count*10); }

3.2 Java变 量 和 函 数 的 实 例

    Java的 类 包 含 变 量 和 函 数。 数 据 变 量 可 以 是 一 些 原 始 的 类 型,如int,char等。 成 员 函 数 是 一 些 可 执 行 的 过 程。 例 如,下 面 程 序 里： public class ClassOne { int i; public ClassOne() { i=10; } public void Add_i(int j) { i=i+j; } } ClassOne包 含 一 个 变 量i和 两 个 成 员 函 数,ClassOne(int first)和Add_i(int j)。

    成 员 函 数 成 员 函 数 是 一 些 可 被 其 它 类 或 自 己 类 调 用 的 处 理 子 程 序。 一 个 特 殊 的 成 员 函 数 叫 构 造 函 数, 这 个 函 数 名 称 一 般 与 本 类 名 程 相 同。 它 没 有 返 回 值。 构 造 函 数 和 成 员 函 数 当 你 在Java里 定 义 一 个 类 时,你 可 定 义 一 个 或 多 个 可 选 的 构 造 函 数,当 创 建 本 类 的 一 个 对 象 时 用 某 一 个 构 造 函 数 来 初 始 化 本 对 象。 用 前 面 的 程 序 例 子 来 说 明,当ClassOne类 创 建 一 个 新 实 例 时, 所 有 成 员 函 数 和 变 量 被 创 建(创 建 实 例)。 构 造 函 数 被 调 用。 ClassOne mc: mc = new ClassOne();

    关 键 词new用 来 创 建 一 个 类 的 实 例,一 个 类 用new初 始 化 前 并 不 占 用 内 存,它 只 是 一 个 类 型 定 义, 当mc对 象 初 始 化 后,mc对 象 里 的i变 量 等 于10。 你 可 以 通 过 对 象 名 来 引 用 变 量i。(有 时 称 为 实 例 变 量) mc.i++;//mc实 例 变 量 加1 因 为mc有ClassOne类 的 所 有 变 量 和 成 员 函 数， 我 们 可 以 使 用 同 样 的 语 法 来 调 用 成 员 函 数 Add_i： Add_i(10); 现 在mc.i变 量 等 于21.

    结 束 函 数 Java并 不 支 持 析 构 函 数(C++里 的 定 义),因 为java本 身 提供 对 象 无 用 时 自 动 清 除 的 功 能,同 时 它 也 提 供了 一 个 自 动 拉 圾 箱 的 成 员 函 数， 在 清 除 对 象 时 被 调 用： Protected void finalize() { close(); }

3.3 对 象 有 效 范 围 和 废 物 自 动 回 收 对 象 有 一 定 的 生 命 期 并 在 它 的 生 命 期 间 使 用 资 源,当 一 个 对 象 不 再 被 使 用 时,它 应 释 放 内 存, 避 免 内 存 溢 出。 在Java里,收 集 和 释 放 内 存 是 一 个 叫 自 动 废 品 回 收 站 的 线 程 的 责 任。 这 个 线 程 监 视 对 象 有 效 范 围 并 给 一 个 走 出 有 效 范 围 的 对 象 作 上 标 识。

    例 如： String s;//没 有 分 配 内 存 s = new String(\"oldstring\");//分 配 内 存 s =\"newstring\";//重 新 分 配 内 存(创 建 新 对 象) 我 们 将 在 以 后 访 问String类 时 将 更 加 明 白 它 的 工 作 过 程,但 它 的 快 速 工 作 过 程 是 这 样 的： 1.创 建 一 个 新 的String类 对 象 并 填 充 以\"oldstring\" 2.创 建 另 一 个String对 象 并 填 充 以\"newstring\" 注 意 我 们 创 建 了 两 个 对 象。 Stirng 对 象 \"oldstring\" Stirng 对 象 \"newstring\" 在 第 三 条 语 句 里,第 一 个 包 括\"oldstring\"的 叫 做s的 对 象 已 走 出 了 有 效 范 围,没 有 任 何 方 法 可 以 再 访 问 他,我 们 现 在 有 一 个 新 的 对 象 也 叫s,包 含\"newstring\"。 在 下 一 个 废 品 回 收 线 程,前 一 个 对 象 将 被 标 识 并 清 除。

3.4 子 类

    子 类 是 利 用 存 在 的 对 象 创 建 一 个 新 对 象 的 机 制,比 如,如 果 你 有 一 个Horse类,你 可 以 创 建 一 个 Zebra子 类,Zebra是Horse的 一 种。

    class Zebra extends Horse { int number_OF_stripes: } 关 键 词extends来 定 义 对 象 有 的 子 类.Zebra是Horse的 子 类。Horse类 里 的 所 有 特 征 都 将 拷 贝 到 Zebra类 里,而Zebra类 里 可 以 定 义 自 己 的 成 员 函 数 和 实 例 变 量。Zebra称 为Horse的 派 生 类 或 继 承。 另 外,你 也 许 还 想 覆 盖 基 类 的 成 员 函 数。 用ClassOne说 明,下 面 是 一 个 派 生 类 覆 盖Add_i功 能 的 例 子.
import ClassOne; public class NewClass extends ClassOne { public void
Add_i(int j) { i=i+(j/2); } }

    当NewClass类 的 实 例 创 建 时,变 量i初 始 化 值 为10,但 调 用Add_i产 生 不 同 的 结 果。 NewClass mnc; mnc=new NewClass(); mnc.Add_i(10); 访 问 控 制 Java里 当 你 创 建 一 个 新 类 时,你 可 以 标 明 变 量 和 成 员 函 数 的 访 问 层 次。

    public public void AnyOneCanAccess(){} public实 例 变 量 和 成 员 函 数 可 以 任 意 其 它 类 调 用。

    protected protected void OnlySubClasses(){} protected实 例 变 量 和 成 员 函 数 只 能 被 其 子 类 调 用.

    private private String CreditCardNumber; private实 例 变 量 和 成 员 函 数 只 能 在 本 类 里 调 用.

    friendly void MyPackageMethod(){} 缺 省 的,如 果 没 有 定 义 任 何 防 火 控 制,实 例 变 量 或 函 数 缺 省 定 义 成friendly,意 味 着 可 以 被 本 包 里 的 任 意 对 象 防 问,但 其 它 包 里 的 对 象 不 可 防 问。 静 态 成 员 函 数 和 变 量 有 ?copy; 时 候,你 创 建 一 个 类,希 望 这 个 类 的 所 有 实 例 都 公 用 一 个 变 量。 也 就 是 说,所 有 这 个 类 的 对 象 都 只 有 实 例 变 量 的 同 一 个 拷 贝。 这 种 方 法 的 关 键 词 是static， 例 如： class Block { 　static int number=50; }
    所 有 从Block类 创 建 的 对 象 的number变 量 值 都 是 相 同 的。 无 任 在 哪 个 对 象 里 改 变 了number的 值, 所 有 对 象 的number都 跟 着 改 变。 同 样 的,你 可 以 定 义static成 员 函 数,但 这 个 成 员 函 数 不 能 访 问 非static函 数 和 变 量。 class Block { static int number = 50; int localvalue; static void add_local(){ localvalue++;//没 有 运 行 } static void add_static() { number++;//运 行 } }

3.5 this和super

    访 问 一 个 类 的 实 例 变 量 时,this关 键 词 是 指 向 这 个 类 本 身 的 指 针,在 前 面ClassOne例 子 中,我 们 可 以 增 加 构 造 函 数 如 下：

    public class ClassOne { int i; public ClassOne() { i = 10; }

    public ClassOne (int value) this.i = value; }

    public void Add_i(int j) { i = i + j; } }

    这 里,this指 向ClassOne类 的 指 针。 如 果 在 一 个 子 类 里 覆 盖 了 父 类 的 某 个 成 员 函 数,但 又 想 调 用 父 类 的 成 员 函 数,你 可 以 用super 关 键 词 指 向 父 类 的 成 员 函 数。
import ClassOne; public class NewClass extends ClassOne { public void Add_i (int j) { i = i+(j/2); super.Add_i (j); } }

    下 面 程 序 里,i变 量 被 构 造 函 数 设 成10,然 后15， 最 后 被 父 类(ClassOne)设 成25。 NewClass mnc; mnc = new NewClass(); mnc.Add_i(10);

3.6 类 的 类 型

    至 今 为 止,我 用 在 类 前 面 只 用 了 一 个public关 键 词,其 实 它 有 下 面4种 选 择：abstract 一 个abstract类 必 须 至 少 有 一 个 虚 拟 函 数,一 个abstract类 不 能 直 接 创 建 对 象,必 须 继 承 子 类 后 才 能。

    final 一 个final类 声 明 了 子 类 链 的 结 尾,用final声 明 的 类 不 能 再 派 生 子 类。 public public类 能 被 其 它 的 类 访 问。 在 其 它 包 里,如 果 想 使用 这 个 类 必 须 先import, 则 它 只 能 在 它 定 义 的package里 使 用。 synchronicable 这 个 类 标 识 表 示 所 有 ?copy; 类 的 成 员 函 数 都 是 同 步 的。

3.7 抽 象 类

    面 向 对 象 的 一 个 最 大 优 点 就 是 能 够 定 义 怎 样 使 用 这 个 类 而 不 必 真 正 定 义 好 成 员 函 数。 如 果 程 序 由 不 同 的 用 户 实 现 时 是 很 有 用 的， 这 不 需 用 户 使 用 相 同 的 成 员 函 数 名。

    在java里Graphics类 里 一 个abstract类 的 例 子 如 下： public abstract class
            Graphics { public abstract void drawLine(int x1,int y1,int x2, int
            y2); public abstract void drawOval(int x,int y,int width, int height);
            public abstract void drawRect(int x,int y,int width, int height);
            ... }

    在Graphics类 里 声 明 了 几 个 成 员 函 数,但 成 员 函 数 的 实 际 代 码 是 在 另 外 一 ?copy; 地 方 实 现 的。

    public class MyClass extends Graphics { public void drawLine (int x1,int y1,int x2,int y2) { <画 线 程 序 代 码> } }

    当 一 个 类 包 含 一 个abstract成 员 函 数,这 个 类 必 须 定 义 为abstract类。 然 而 并 不 是abstract类 的 所 有 的 成 员 函 数 都 是abstract的。Abstract类 不 能 有 私 有 成 员 函 数(它 们 不 能 被 实 现),也 不 能 有 静 态 成 员 函 数。

3.8 接 口

    当 你 确 定 多 个 类 的 操 作 方 式 都 很 相 象 时，abstract成 员 函 数 是 很 有 用 的。 但 如 果 你 需 要 使 用 这 ?copy;abstract成 员 函 数， 必 须 创 建 一 个 新 类， 这 样 有 时 很 繁 琐。 接 口 提 ?copy; 了 一 种 抽 象 成 员 函 数 的 有 利 方 法。 一 个 接 口 包 含 了 在 另 一 个 地 方 实 现 的 成 员 函 数 的 收 集。 成 员 函 数 在 接 口 里 定 义 为public和 abstract。 接 口 里 的 实 例 变 量 是public,static和final。 接 口 和 抽 象 的 主 要 区 别 是 一 个 接 口 提 ?copy; 了 封 装 成 员 函 数 协 议 的 方 法 而 不 必 强 迫 用 户 继 承 类。 例 子： public interface AudiClip { //Start playing the clip. void play(); //Play the clip in a loop. void loop(); //Stop playing the clip void stop(); }

    想 使 用Audio Clip接 口 的 类 使 用implenents关 键 词 来 提 ?copy; 成 员 函 数 的 程 序 代 码。 class MyClass implements AudioClip { void play(){ <实 现 代 码> } void loop <实 现 代 码> } void stop <实 现 代 码> } }

    优 点 一 个 接 口 类 可 以 被 任 意 多 的 类 实 现， 每 个 类 可 以 共 享 程 序 接 口 而 不 必 关 心 其 它 类 是 怎 样 实 现 的。 class MyOtherClass implements AudioClip { void stop(){ <实 现 代 码> } ... } 内 部 成 员 函 数 Java还 提 ?copy; 了 调 用C和C++函 数 的 方 法。 用native关 键 词 来 定 义C和C++的 函 数。 public class Date { int now; public Date() { now = time (); } private native int time (); static { System.loadLibrary(\"time\"); } } 一 ?copy;Java代 码 写 好 后,就 需 要 以 下 步 骤 执 行： 1.用javah来 创 建 头 文 件(.h) 2.用javah来 创 建stub文 件 3.用C和C++写native成 员 函 数 的 代 码 4.编 译stub文 件 和.C文 件 成 一 个 动 态 可 加 载 库 5.用java运 行java程 序 或appletviewer运 行applet 注 意：Native成 员 函 数 超 出 了 类 的 范 围。

3.9 包(Packages)

    包(Package)由 一 组 类(class)和 界 面(interface)组 成。 它 是 管 理 大 型 名 字 空 间， 避 免 名 字 冲 突 的 工 具。 每 一 个 类 和 界 面 的 名 字 都 包 含 在 某 个 包 中。 按 照 一 般 的 习 惯， 它 的 名 字 是 由\".\" 号 分 隔 的 单 词 构 成， 第 一 个 单 词 通 常 是 开 发 这 个 包 的 组 织 的 名 称。

    定 义 一 个 编 译 单 元 的 包 编 译 单 元 的 包 由package语 句 定 义。 如 果 使 用package语 句， 编 译 单 元 的 第 一 行 必 须 无 空 格， 也 无 注 释。 其 格 式 如 下： package packageName; 若 编 译 单 元 无package语 句， 则 该 单 元 被 置 于 一 个 缺 省 的 无 名 的 包 中。 使 用 其 它 包 中 的 类 和 界 面 在Java语 言 里 提 ?copy; 一 个 包 可 以 使 用 另 一 个 包 中 类 和 界 面 的 定 义 和 实 现 的 机 制。 用import关 键 词 来 标 明 来 自 其 它 包 中 的 类。 一 个 编 译 单 元 可 以 自 动 把 指 定 的 类 和 界 面 输 入 到 它 自 己 的 包 中。 在 一 个 包 中 的 代 码 可 以 有 两 种 方 式 来 定 义 来 自 其 它 包 中 的 类 和 界 面： * 在 每 个 引 用 的 类 和 界 面 前 面 给 出 它 们 所 在 的 包 的 名 字; //前 缀 包 名 法 acme. project.FooBar obj=new acme. project. FooBar( ); * 使 用import语 句， 引 入 一 个 类 或 一 个 界 面， 或 包 含 它 们 的 包。 引 入 的 类 和 界 面 的 名 字 在 当 前 的 名 字 空 间 可 用。 引 入 一 个 包 时， 则 该 包 所 有 的 公 有 类 和 界 面 均 可 用。 其 形 式 如 下： // 从 acme.project 引 入 所 有 类 import acme.project.*; 这 个 语 句 表 示acme.project中 所 有 的 公 有 类 被 引 入 当 前 包。 以 下 语 句 从acme. project包 中 进 入 一 个 类Employec_List。 //从 acme. project而 引 入 Employee_List import acme.project.Employee_list; Employee_List obj = new Employee_List( ); 在 使 用 一 个 外 部 类 或 界 面 时， 必 须 要 声 明 该 类 或 界 面 所 在 的 包， 否 则 会 产 生 编 译 错 误。 import(引 用) 类 包(class package)用import关 键 词 调 入,指 定package名 字 如 路 径 和 类 名,用*匹 配 符 可 以 调 入 多 于 一 个 类 名。
import java.Date; import java.awt.*;

    如 果java源 文 件 不 包 含package,它 放 在 缺 省 的 无 名package。 这 与 源 文 件 同 目 录， 类 可 以 这 样 引 入: import MyClass。

    Java系 统 包： Java语 言 提 ?copy; 了 一 个 包 含 窗 口 工 具 箱， 实 用 程 序， 一 般I/O,工 具 和 网 络 功 能 的 包。

    java.applet 这 个 包 包 含 量 了 一 ?copy; 设 计applet的 类,用 一 个 类Applet和 三 个 接 口. AppletContext;AppletStub;和AudioClip.

    java.awt 另 一 个 窗 口 工 具 箱 包.awt,包 含 了 一 ?copy; 产 生 装 饰 物 和GUI成 员 的 类。 这 个package包 括：

    Button,Checkbox,Choice,Component,Graphics,Menu,Pane1,TextArea和 TextField。

    java.io I/O package包 含 文 件 输 入/输 出 类，FileInput Stream和File OutputStream.

    java.lang 这 个 包 包 含Java语 言 类,包 含:对 象,线 程,异 常 出 口,系 统,整 数,原 点,数 学,字 符 等。

    java.net 这 个 类 支 持TCP/IP网 络 协 议， 并 包 含Socket类,URL和URL相 联 系 的 类。

    java.util 这 个 类 包 含 一 ?copy; 程 序 的 同 步 类,它 包 含Date,Dictionary类 等。

3.10 异 常

    当 在Java程 序 中 发 生 一 个 错 误 时， 例 如： 一 个 变 元 的 值 非 法， 代 码 会 发 现 这 个 错 误， 并 引 发 一 个 异 常(exception)。 在 缺 省 的 情 况 下， 异 常 会 输 出 一 个 错 误 消 息， 然 后 中 止 线 程 的 执 行。 但 是， 程 序 自 己 可 以 定 义 异 常 处 理 段(exception handler)来 截 获(catch)异 常， 并 从 错 误 中 恢 复。 有 一 ?copy; 异 常 是 由Java解 释 器 在 运 行 时 刻 引 发 的。 实 际 上， 任 何 类 都 可 以 定 义 属 于 自 己 的 异 常， 并 使 用throw语 句 引 发 它 们。 一 个throw（引 发?copy; 语 句 是 由throw关 键 字 和 一 个 对 象 构 成。 按 常 规， 该 对 象 应 该 是Exception 类 的 实 例 或 其 子 类 的 实 例。throw语 句 会 引 起 执 行 转 向 相 应 的 异 常 处 理 段。 当 一 个throw语 句 执 行 时， 它 下 面 的 所 有 代 码 不 再 执 行 了， 它 所 在 的 方 法 也 不 再 返 回 值。 下 面 的 例 子 将 演 示 如 何 创 建 一 个Exception的 子 类， 然 后 引 发 一 个 异 常。 class MyException extends Exception { } class MyClass { void oops() { if ( /* 不 出 现 错 误 */) { … } else { /* 出 错 */ } else { /* 出 错 */ throw new MyException( ); } } } 为 了 定 义 一 个 异 常 处 理 段， 程 序 必 须 用try语 句 把 可 能 产 生 异 常 的 代 码 成 组。 在try语 句 后 面 跟 上 一 个 或 多 个catch(截 获?copy; 语 句， 每 个 异 常 对 应 一 个catch语 句。 每 个catch语 句 中 包 含 着 异 常 处 理 段。 例 如： try { p.a=10; } catch ( NullPointerException e) { println(\"p was null\"); } catch ( Exception e) { println (\"other errors occured\"); } catch ( Object obj) { println(\"Who threw that object?\"); } catch语 句 和 一 个 方 法 定 义 类 似， 只 不 过 该 方 法 只 有 一 个 参 数， 且 无 返 回 类 型。 参 数 可 以 是 一 个 类 或 一 个 界 面。 当 一 个 异 常 发 生 时， 嵌 套 的try/catch语 句 会 寻 找 出 与 该 异 常 类 相 匹 配 的 参 数。 如 果 一 个 参 数 和 指 定 异 常 匹 配 则： * 该 参 数 和 指 定 的 异 常 是 同 一 个 类， 或 * 该 参 数 是 指 定 异 常 的 子 类， 或 * 如 果 参 数 是 一 个 界 面， 指 定 异 常 类 实 现 了 这 个 界 面。 第 一 个 参 数 和 异 常 匹 配 的try/catch语 句， 则 与 其 匹 配 的catch语 句 执 行。 在catch语 句 执 行 完 后， 程 序 的 执 行 被 恢 复。 但 已 不 可 能 恢 复 到 异 常 发 生 处 再 次 执 行。 例 如： print ( \"now\"); try { print (\"is\"); throw new MyException( ); print (\"a\"); } catch (MyException e) { print (\"the \"); } print (\"time\\n\"); 打 印 为\"now is the time\"。 正 如 这 个 例 子 所 示， 异 常 应 该 主 要 用 于 错 误 处 理， 若 用 于 其 它 方 面 会 使 代 码 晦 涩 难 ?reg;。 异 常 处 理 段 是 可 以 嵌 套 的， 允 许 异 常 处 理 可 以 发 生 在 多 个 地 方。 嵌 套 异 常 处 理 通 常 用 于 当 第 一 个 处 理 程 序 无 法 完 全 从 错 误 中 恢 复 过 来 的 时 候， 而 不 得 不 执 行 一 ?copy; 清 除 代 码。 为 了 把 异 常 处 理 控 制 传 递 给 更 高 层 的 处 理 段， 可 以 再 一 次 对 截 获 对 象 实 ?copy;throw操 作。 注 要 再 次 实 throw异 常 的 方 法，throw语 句 执 行 完 后， 会 终 止 执 行。 try { f. open ( ); } catch(Exception e) { f. close( ); throw e; } 定 局 语 句 finally（定 局?copy; 语 句 是 用 于 保 证 无 论 在 异 常 是 否 发 生 的 情 况 下， 某 ?copy; 代 码 被 执 行。 下 例 说 明finally语 句 的 用 法： try { //做 某 ?copy; 动 作； ｝ finally { //此 后 清 除； } 和 以 下 代 码 类 似 try { //做 某 ?copy; 动 作 } catch (Object e) { //此 后 清 除； throw e; } } //此 后 清 除； 即 使try块 中 包 含return，break，continue，throw语 句，finally语 句 也 会 被 执 行。 例 如： 下 面 的 代 码\"finally\" 总 是 被 输 出， 而\"aftertry\" 仅 在a!=10时 被 输 出。 try { if (a==10) { return ; } } finally { print (\"finally\\n\"); ｝ print (\"after try \\n\"); 运 行 时 刻 异 常 本 节 列 出 的 清 单 是Java解 释 器 引 发 的 各 种 异 常。 当 运 行 时 刻 发 现 各 种 错 误， 由 解 释 器 引 发 异 常。 ArithmeticException 如 果 程 序 试 图 除0， 或 用0取 模， 会 产 生ArithmeticException（算 术 异 常?copy;， 其 它 算 术 操 作 不 会 产 生 异 常。 有 关Java如 何 处 理 其 它 算 术 错 误 的 信 息， 见\" 整 数 运 算 符\" 和\"浮点 运 算 符\" 两 节。 例 如： 下 面 的 代 码 将 会 引 发ArithmeticException异 常： class Arith { public static void main (String args [ ] ) { int j = 0; j = j/j; } } NullPointerException 当 程 序 试 图 访 问 一 个 空 对 象 中 的 变 量 或 方 法， 或 一 个 空 数 组 中 的 元 素 时 则 引 发 NullPointerException（空 指 针 异 常?copy;。 例 如， 访 问 长 度 为0的 数 组a[0]。 有 以 下 类 声 明， 运 行 时 会 引 发NullPointerException异 常： class Null { public static void main(String args [ ]) { String o = null; int a [ ] = null; o.length( ); a[0] = 0; } } 有 趣 的 是， 如 果 我 们 引 发 一 个 空 对 象， 也 会 产 一NullPointerException异 常。 IncompatibleClassChangeException 当 一 个 类 的 定 义 被 改 变， 而 引 用 该 类 的 其 它 类 没 有 被 重 新 编 译 时， 会 产 生 这 一 异 常。 有 四 种 类 更 改 会 导 致 运 行 时 刻 引 发IncompatibleClassChangException异 常。 * 一 个 类 中 的 变 量 声 明 由static变 成 非static， 而 其 它 访 问 该 类 这 一 变 量 的 类 没 有 被 重 新 编 译。 * 一 个 类 中 的 变 量 声 明 由 非static变 成static， 而 其 它 访 问 该 类 这 一 变 量 的 类 没 有 被 重 新 编 译。 * 类 中 声 明 的 某 个 域 被 删 除， 而 其 它 访 问 该 域 的 类 没 有 被 重 新 编 译。 * 类 中 声 明 的 某 个 方 法 被 删 除， 而 其 它 访 问 该 方 法 的 类 没 有 被 重 新 编 译。

    ClassCastException 如 果 试 图 把 对 象o强 制 成Class C， 而o既 不 是Class C的 实 例， 也 不 是Class C子 类 的 实 例， 这 时 便 会 产 生ClassCastException。 class ClassCast { public static void main (String args [ ] ) { Object o = new Object( ); String s = (string) o; s.length( ); } } } NagativeArraySizeException 如 果 一 个 数 组 的 长 度 是 负 数， 则 会 引 发NagativeArraySizeException（数 组 负 下 标?copy; 异 常。 例 如 下 面 类 定 义 的 代 码 在 运 行 时 引 发 这 一 异 常： class NegArray { public static void main(String args [ ]) { int a [ ] = new int [-1]; a[0] = 0; } }

    OutOfMemoryException 当 系 统 无 法 再 向 应 用 程 序 提 ?copy; 内 存 时， 会 引 发OutOfMemoryException（内 存 溢 出?copy; 异 常。 这 种 异 常 只 能 出 现 在 创 建 新 对 象 的 时 候， 即new被 调 用 的 时 候。 例 如， 下 面 一 段 代 码 在 运 行 时 刻 会 引 发OutOfMemoryException异 常： class Link { int a [ ] = new int [1000000]; Link l; } Class OutOfMem
{ public static void main(String args [ ]) { public static void main(String args [ ]) { Link root = new link( ); Link cur = root; while (true) { cur.l = new Link( ); cur = cur.l; } } } NoClassDefFoundException 如 果 一 个 类 被 引 用， 但 在 运 行 时 刻， 系 统 没 有 找 到 被 引 用 的 类， 这 时 会 引 发 NoClassDefFoundException（未 找 到 类 定 义?copy; 异 常。 例 如，NoClass
的 声 明 如 下： class NoClass { public static void main(String args [ ]) { C c = new C ( ); } } 当NoClass运 行 时， 如 果 解 释 器 找 不 到C类， 则 会 产 生NoClassDefFoundException。 注 意， 在NoClass被 编 译 时C类 一 定 要 存 在。

    IncompatibleType Exception 如 果 试 图 为 一 界 面 作 实 例， 则 会 引 发IncompatibleTypeException（类 型 不 兼 容?copy; 异 常。 例 如， 下 面 的 代 码 会 引 发 一 个IncompatibleTypeException。 Interface I { } class IncompType { public static void main(String args [ ]) { I r = (I) new (\"I\"); } } ArrayIndexOutOfBoundsException 试 图 访 问 数 组 中 的 一 个 非 法 元 素 时， 会 引 发ArrayIndexOutOfBoundsException（数 组 索 引 越 界?copy; 异 常。 例 如： Class ArrayOut { public static void main(String args [ ]) { int a [ ]=new int[0]; a[0]=0; } } public static void main(String args [ ]) { int a [ ]=new int[0]; a[0]=0; } } UnsatisfiedLinkException 如 果 一 个 方 法 被 声 明 为 本 机， 但 该 方 法 在 运 行 时 刻 却 不 能 连 接 到 一 个 例 程 体 上 去 时， 会 产 生 UnsatisfiedLinkException（无 法 连 接?copy; 异 常。 例 如： Class NoLink { static native void foo( ); public static void main(String args [ ]) { foo( ); } } InternalException InternalException（内 部?copy; 异 常 是 不 能 被 引 发 的。 只 有 在 运 行 失 败 作 一 致 性 检 查 时， 才 会 引 发 这 个 异 常。

本 章 小 结

    1. Java语 言 的 基 本 结 构 象C/C++。 2. Java语 言 的 源 程 序 代 码 由 一 个 或 多 个 编 译 单 元(compilation unit)组 成。


第 四 章 Java应 用 程 序 的 基 本 框 架

    学 习 一 门 新 语 言 最 好 是 先 看 几 个 简 单 的 程 序 例 子。 下 面 我 们 将 看 到 几 个 非 常 基 本 的 程 序 例 子。

4.1 Java应 用 程 序 的 运 行 环 境

    Java应 用 程 序 是 指 可 以 独 立 运 行 在Java虚 拟 机 上 的 程 序， 它 是 一 种 中 间 代 码（byte-code?copy;。 比 如 你 的 应 用 程 序 叫my.java， 程 序 里 有 一 个 名 称 为app1的 类， 用Javac或 其 它 编 译 器， 编 译 后 将 会 生 成app1.class， 则 在 命 令 行 状 态 下 输 入：java app1就 可 以 运 行 此 程 序。 注 意， 用java 命 令 运 行 的 类 必 须 有main函 数， 否 则 不 能 执 行。 与 普 通java应 用 程 序 不 同 的 另 一 种 另 一 种Java程 序 叫Java Applet。 我 们 把 它 译 成Java小 程 序， 这 种 程 序 后 缀 也 是.class， 但 它 不 能 直 接 在java虚 拟 机 上 运 行， 也 就 是 输 入java *.class不 能 运 行， 这 种 程 序 里 可 以 没 有main函 数， 它 必 须 由 某 个 浏 览 器 来 运 行， 比 如Appletviewer或 Netscape2.02以 上 等。 这 种 程 序 我 们 将 在 后 面 章 节 中 介 绍。

4.2 最 简 单 的Java程 序 解 释

    让 我 们 来 看 一 看 最 简 单 的Java应 用 程 序 例 子， 来 理 解 一 下 它 的 结 构： Filename:1.java class myfirst{ public static void main(String args[ ]) { System.out,println (\"This is my first Java Application\"); } } 这 就 是 一 个 完 整 的 Java 应 用 程 序， 将 它 编 译： Javac 1.java 在 当 前 目 录 下， 它 将 生 成myfirst.class 文 件， Java myfirst 屏 幕 上 将 会 输 出：This is my first Java Application 让 我 们 来 一 步 一 步 分 析 每 句 话 含 义 （1?copy; class myfirst 这 一 行 用 关 键 词class来 定 义 名 为myfirst的 新 类，myfirst是 新 类 的 名 称， 必 须 是 一 个 有 效 的 标 识 符， 有 效 标 识 符 定 义 请 见 程 序 设 计 基础章 节。 类 的 说 明 包 括 数 据 说 明 和 成 员 函 数 说 明， 都 放 在 类 后 面 的 大 括 号 里 面。 一 般 类 定 义 如 下： class 类 名 称｛ 数 据 定 义； 函 数 定 义； }

    ( 2 )public static void main (String args [ ] ) public是 一 个 表 示 访 问 权 限 的 关 键 字， 表 示 此 成 员 函 数 是 公 有 的， 可 以 被 其 他 类 直 接 调 用， 包 括java解 释 器。 相 对 应 的 关 键 字 有private和protected，friend。private表 示 只 能 被 本 类 访 问，protected表 示 只 能 被 子 类 访 问，friend是 缺 省 的 访 问 权 限， 表 示 能 被 本 包(package)中 任 意 类 访 问， 对 其 它 包 中 的 类 是 不 可 访 问 的。

    static 表 示main 成 员 函 数 在myfirst类 的 所 有 对 象 中 是 唯 一 的， 因 此 如 果 本 程 序 生 成 另 一 个myfirst类 对 象， 调 用 的 main 函 数 将 是 同 一 个 函 数。 void 表 示 main 函 数 没 有 返 回 值， 如 果 有 返 回 类 型 值， 则 可 加 上interger 或boolean 诸 如 此 类， 对 于 有 返 回 值 的 函 数， 其 函 数 实 体 的 最 后 应 加 上return语 句。 main 这 个 函 数 是 这 运 行 应 用 程 序 的 入 口 点， 因 此 编 写 应 用 程 序 是 必 须 有main( )函 数， 且 是 唯 一 的。 （3?copy; System.out.println 一 句 这 一 句 是main函 数 里 的 功 能 语 句， 是 调 用Java里 System 包 里 的out 类 的println成 员 函 数， 是 标 准 输 入 输 出。

4.3 Java应 用 程 序 参 数 的 传 递

    Java语 言 很 类 似C和C++语 言。 在C语 言 里， 通 过 在 命 令 行 输 入 参 数，C程 序 可 由main函 数 读 入 这 ?copy; 参 数，java程 序 也 一 样， 请 看 下 面 程 序： Filename:2.java class My2 { public static void main (String args[ ] ) { int arc = args.length; if (arc>0) { for (int i =0;i ) { m = new Memo(args[0]); } else { m = new Memo(); } m.start(); } } 将Frame联 系 起 来 l 熟 悉 的 函 数 paint()和mouseDown()， 看 起 来 有 点 眼 熟。 这 ?copy; 函 数 与applet的 函 数 一 样。 实 际 上， 一 个frame 包 含 各 种GUI组 件 与applet的 形 式 一 样。 另 一 个 熟 悉 的 函 数 是start()。 这 个 函 数 并 不 必 要， 因 为 我 们 没 有 覆 盖 任 何 已 存 在 的 函 数。 但 你 想 继 承applet编 程 的 风 格， 你 还 是 可 以 使 用start(),stop(),init()与destroy()等 函 数。 l 新 函 数 我 们 熟 悉 的 函 数start()调 用 了show()。show()函 数 是Window类 的 继 承， 它 显 示Fame及 其 所 有 组 件。 在mouseDown()函 数 里 我 们 看 到 两 个 函 数：hide()和dispose()。hide()只 简 单 地 使Frame不 可 见。 你 可 以 在 任 何 时 候 调 用 它 来 隐 藏 窗 口。 dispose()函 数 释 放 由Frame占 有 的 系 统 资 源。 只 有 在 你 不 需 要Frame时 才 调 用 它。

    l 构 造 函 数 Memo例 子 还 包 含 了 一 个 新 函 数Memo()。 其 实 这 个 类 有 两 个Memo()函 数！ 任 何 与 类 名 相 同 的 函 数 都 是 构 造 函 数。 它 在 创 建 一 个 新 对 象 时 被 调 用。 你 可 以 认 为 它 是 一 个 对 象 的init()初 始 化 函 数。 为 什 么 需 要 两 个 构 造 函 数 呢？ 有 了 两 个 构 造 函 数， 我 们 可 以 利 用 多 态 性 的 优 点， 有 两 种 方 法 创 建 一 个 新Memo对 象。 我 们 可 以 简 单 的 创 建 使 用 缺 省 信 息 的Memo对 象： m = new Memo(); 或 者， 我 们 可 以 自 己 提 ?copy; 信 息： m = new Memo(\"Our message\"); Frame控 制 本 程 序 的 最 后 功 能 是 在main()里 建 立 并 显 示frame。 它 由 以 下 两 步 实 现： //Step 1 m = new Memo(); //Step 2 m.start(); 第 一 步 初 始 化memo对 象。 我 们 象 操 作 其 它 对 象 一 样 操 作m。 为 显 示frame， 我 们 需 要 调 用 show()。 这 在 第 二 步 里 实 现。 另 一 个 要 注 意 的 是 程 序 的健 壮 性： 我 们 基 本 的frame里 没 有 包 含 处 理 标 准 WINDOW_DESTROY消 息 的 函 数。 这 样， 你 将 不 能 在 窗 口 的 控 制 菜 单 里 选 中\"Quit\"或\"Exit\"。 为 了 增 加 这 个 功 能， 你 需 要 增 加 以 下 的 处 理 程 序： public boolean handleEvent (Event e) { if (e.id == Event.WINDOW_DESTROY) { dispose(); System.exit(1); return true; } else { //Go ahead and do what we normally would have done return super.handleEvent(e); } }

    菜 单 图 形 界 面 依 靠 菜 单 来 指 导 用 户 操 作。 设 计 独 立 的Java应 用 程 序 时，Java提 ?copy; 创 建 和 使 用 菜 单 的 直 接 方 法。 象 其 它 组 件 一 样，new将 创 建 一 个 菜 单： Menu optionMenu; optionsMenu = new Menu(\"Options\"); 菜 单 项 一 ?copy; 你 创 建 了 一 个 菜 单， 你 可 以 使 用add()来 组 建 菜 单 项： optionsMenu.add(new MenuItem(\"Option1\");optionsMenu.add(new MenuItem(\"Option2\"); 菜 单 事 件 当 你 选 中 某 个 菜 单 项 时， 你 创 建 此 菜 单 项 的 字 符 ?reg; 将 在 事 件 中 返 回。 你 可 以 象 测 试 按 钮 选 择 一 样 测 试 菜 单 选 择： public boolean action (Event e, Object arg) { ... if (e.target instanceof MenuItem) { System.out.println((String) arg); } ... } 其 它 菜 单 项 除 了 上 面 描 述 的 简 单 菜 单 项 外， 你 还 可 增 加CheckBox菜 单 项， 分 割 线， 以 及 子 菜 单。 下 面 是 一 ?copy; 例 子： Menu m,n; m = new Menu(\"Examples\"); m.add(new MenuItem(\"Basic\")); m.add(new MenuItem(\"Simple\"));
//add a separator m.add(new MenuItem(\"-\")); //add a Checkbox item m.add(new CheckboxMenuItem(\"Check\")); //add a submenu n = new Menu(\"More Examples\");n.add(new MenuItem(\"Sub Basic\")); n.add(new MenuItem(\"Sub Simple\")); m.add(n); 菜 单 条 你 创 建 好 菜 单 后， 你 应 将 ?reg; 放 在 应 用 程 序 的 菜 单 条 上： mb = new Menubar(); mb.add(m); mb.add(optionsMenu); 然 后 你 可 为applet设 置 菜 单 条： setMenuBar(mb);

4.5 独 立 应 用 程 序 例 子

    为 了 看 一 个 更 复 杂 的 独 立 的 图 形 界 面 应 用 程 序， 下 面 有 一 个 数 字 转 换 的 例
            子： import java.awt.*;
public class d2x extends Frame { int decimalValue= 0; String baseXValue
= new String (\"0\"); TextField dDisplay,xDisplay;
//d2x constructor public d2x() { super(\"Decimal Converter\");//set
            the
title of the frame MenuBar mb = new MenuBar(); Button d2Binary = new
Button(\"Binary\"); Button d2Octal = new Button(\"Octal\");
            Button d2Hex =
new Button(\"Hex\"); Button d2Base36 = new Button(\"Base36\");
            Panel p1 = new
Panel(); Panel p2 = new Panel(); Panel p3 = new Panel();
//add a simple menu Menu m = new Menu(\"Application\");
            m.add(new
CheckboxMenuItem(\"Base 36 Active\"); m.add(new MenuItem(\"Exit\"));
//add menu to menubar mb.add(m); setMenuBar(mb);//install this menu
            bar
in the frame
//Add buttons to their own panel p3.setLayout(new FlowLayout());
p3.add(d2Binary); p3.add(d2Octal); p3.add(d2Hex); p3.add(d2Base36);
//Add text fields Label dLabel = new Label(\"Enter Deecimal:
            \"); Label
xLabel = new Label(\"Converted Value: \"); dDisplay = new
TextField(integer.toString(decimalValue),7); xDisplay = new
TextField(baseXValue,32); xDisplay.setEditable(false);
p1.setLayout(new FlowLayout(FlowLayout.LEFT)); p2.setLayout(new
FlowLayout(FlowLayout.LEFT)); p1.add(dLabel); p1.add(dDisplay);
p2.add(xLabel); p2.add(xDisplay);
//Add the panels add(\"North\",p1); add(\"Center\",p2);
add(\"South\",p3); }//end d2x constructor
public void start() { resize(400,150); show(); }
public void updateXDisplay() { xDisplay.setText(baseXValue); }
public boolean handleEvent(Event evt) { if (evt.targt intanceof MenuItem)
{ if (\"Exit\".equals(((MenuItem)evt.target).getLabel()))
            { hide();
dispose(); System.exit(0); return false; } retrun true; } else if
(evt.target instanceof Button) { String whick =
((Button)evt.target).getLabel(); if (whick.equals(\"Binary\"))
{ decimalValue = Integer.parseInt(dDisplay.getText()); baseXValue
            =
Interger.toString(decimalValue,2); } if (whick.equals(\"Octal\"))
{ decimalValue = Integer.parseInt(dDisplay.getText()); baseXValue
            =
Interger.toString(decimalValue,8); } if (whick.equals(\"Hex\"))
{ decimalValue = Integer.parseInt(dDisplay.getText()); baseXValue
            =
Interger.toString(decimalValue,16); } if (whick.equals(\"36\"))
{ decimalValue = Integer.parseInt(dDisplay.getText()); baseXValue
            =
Interger.toString(decimalValue,36); } updateXDisplay(); return true;
            }
return false; }
public static void main(String args[]) { d2x m = new d2x(); m.start();
            } }

本 章 小 结：

    1.java有 两 种 类 型 的 应 用 程 序， 一 种 是 直 接 运 行 在java虚 拟 机 上， 用java命 令 执 行； 另 一 种 运 行 在 浏 览 器 里， 由 浏 览 器 调 用 执 行， 一 般 称 它 为Applet小 程 序。 本 书 主 要 讨 论 第 一 种 应 用 程 序。 2.java应 用 程 序 是 由 类 组 成 的， 而 且 用java命 令 行 执 行 的 类 必 须 有main入 口 函 数。 3.与C语 言 相 似，java程 序 也 可 由 命 令 行 传 递 给main函 数 参 数。 4.基 本 窗 口java程 序 的 基 本 类 是Frame。 利 用 它 可 以 很 方 便 地 建 立 图 形 用 户 界 面 程 序。


第 五 章 Java的 类

类 是Java语 言 面 向 对 象 编 程 的 基 本 元 素， 它 定 义 了 一 个 对 象 的 结 构 和 行 为。 在Java程 序 里， 你 要 表 达 的 概 念 封 装 在 某 个 类 里。 一 个 类 定 义 了 一 个 对 象 的 结 构 和 它 的 功 能 接 口， 功 能 接 口 称 为 成 员 函 数。 当Java程 序 运 行 时， 系 统 用 类 的 定 义 创 建 类 的 实 例， 类 的 实 例 是 真 正 的 对 象。 类 定 义 的 一 般 形 式 如 下：
class classname extends superclassname { type instance-variable1; type instance-variable2; ................................. type instance-variableN; type methodname1(parameter-list) { method-body; } type methodname2(parameter-list) { method-body; } ....................................................type methodnameN(parameter-list) {method-body; } }

这 里，classname和superclassname是 合 法 的 标 识 符。 关 键 词extends用 来 表 明classname是 superclassname派 生 的 子 类。 有 一 个 类 叫 做Object， 它 是 所 有Java类 的 根。 如 果 你 想 定 义Object 的 直 接 子 类， 你 可 以 省 略extends子 句， 编 译 器 会 自 动 包 含 它。 下 面 是 一 个 简 单 的 类 的 定 义。 class University { }

5.1 对 象 实 例

类 名 可 以 作 为 变 量 的 类 型 来 使 用， 如 果 一 个 变 量 的 类 型 是 某 个 类， 那 么 它 将 指 向 这 个 类 的 实 例， 称 为 对 象 实 例。 所 有 对 象 实 例 和 它 们 的 类 型（某 个 类?copy; 的 子 类 的 实 例 都 是 相 容 的。 就 象 可 以 把byte型 的 值 赋 给int型 的 变 量 一 样， 你 可 以 把Object的 子 类 的 任 何 实 例 赋 给 一 个Object型 的 变 量。 一 个 实 例 是 类 模 板 的 单 独 的 拷 贝， 带 有 自 己 的 称 为 实 例 变 量 的 数 据 集。 每 个 实 例 也 可 以 作 为 一 个 对 象。 当 你 定 义 一 个 变 量 的 类 型 是 某 个 类 时， 它 的 缺 省 值 是null，null是Object的 一 个 实 例。 对 象null没 有 值， 它 和 整 数0不 同。 下 面 这 个 例 子 中， 声 明 变 量u的 类 型 是 类University。 University u; 这 里， 变 量u的 值 是null。

5.2 实 例 变 量

Java通 过 在 类 定 义 的 大 括 号 里 声 明 变 量 来 把 数 据 封 装 在 一 个 类 里。 这 里 的 变 量 称 为 实 例 变 量。 下 面 的 例 子 定 义 了 一 个 叫 做University的 类， 它 有 两 个 实 例 变 量：name和city。 class University{ String name, city; }

5.3 new操 作 符

操 作 符new用 来 生 成 一 个 类 的 实 例， 下 面 这 个 例 子 生 成 了 类University的 一 个 实 例， 存 放 在 变 量u中。

University u = new University( ); 在 此 例 中， 变 量u指 向 这 个 对 象， 但 并 不 真 正 包 含 这 个 对 象。 你 可 以 用 多 个 变 量 指 向 同 一 个 对 象。 下 面 的 例 子 中， 创 建 了 一 个University的 对 象， 但 创 建 了 两 个 指 向 它 的 变 量。
University u = new University(); University u2 = u;
对u2所 指 向 的 对 象 的 任 何 改 动 都 会 对u所 指 向 的 对 象 起 作 用， 因 为 它 们 是 同 一 个 对 象。 对u和u2的 赋 值 只 是 把 它 们 指 向 这 个 对 象， 既 没 有 分 配 内 存， 也 没 有 复 制 这 个 对 象 的 任 何 部 分。 对u的 再 赋 值 只 是 简 单 地 去 掉 了u和 原 来 对 象 的 联 系， 并 不 影 响 对 象 本 身， 下 面 的 例 子 说 明 了 这 种 情 况。
University u = new University( ); University u2 = u; u = null;

尽管u被 赋 值 为null，u2仍 指 向 原 来 由 操 作 符new创 建 的 对 象。 在 前 面 的 例 子 里， 我 们 生 成 了 一 个 对 象 并 且 指 向 了 它 两 次。 这 就 允 许 两 个 变 量 改 变 同 一 个 对 象。 创 建 一 个 新 的 对 象 时， 可 直 接 对 它 的 实 例 变 量 赋 值。 每 个 对 象 都 有 它 所 属 类 的 实 例 变 量 的 拷 贝， 每 个 对 象 的 实 例 变 量 都 是 和 其 他 对 象 的 实 例 变 量 分 离 的， 所 以 改 变 一 个 对 象 的 实 例 变 量 不 会 影 响 其 他 对 象 的 实 例 变 量。 下 面 的 例 子 创 建 了 两 个University的 对 象， 并 对 它 们 分 别 赋 值： class TwoUniversity { public static void main(String args[]) { University u1 = new University( ); University u2 = new University( ); u1.name = \"北 ?copy; 大 学\"; u1.city = \"北 ?copy;\"; u2.name = \"清 华 大 学\"; u2.city = \"北 ?copy;\"; System.out.println(\"大 学：\" + u1.name + \" 城 市：\" + u1.city); System.out.println(\"大 学：\" + u2.name + \" 城 市：\" + u2.city); } } 这 个 例 子 创 建 了 两 个University的 对 象， 并 且 对 它 们 的name、city分 别 赋 了 不 同 的 值， 这 说 明 这 两 个 对 象 是 真 正 分 离 的。 下 面 是 该 程 序 运 行 后 的 输 出 结 果。 C:\\>java TwoUniversity 大 学： 北 ?copy; 大 学 城 市： 北 ?copy; 大 学： 清 华 大 学 城 市： 北 ?copy;

5.4 点（.?copy; 操 作 符点（.?copy; 操 作 符 用 来 接 收 一 个 对 象 的 实 例 变 量 和 成 员 函 数。 下 面 是 用 点 操 作 符 来 接 收 实 例 变 量 的 一 般 形 式。 objectreference.variablename
这 里objectreference是 一 个 对 象 实 例，variablename是 这 个 对 象里 你 想 接 收 的 实 例 变 量。 下 面 的 程 序 段 说 明 了 怎 样 用 点 操 作 符 来 给 实 例 变 量 赋 值。
u.name = \"北 ?copy; 大 学\"; u.city = \"北 ?copy;\"; 下 面 说 明 怎 样 用 点 操 作 符 来 得 到 实 例 变 量 的 值。 System.out.println(\"大 学：\" + u.name + \" 城 市：\" + u.city); 通 过 向 类University里 加 入 一 个 成 员 函 数main， 我 们 创 建 了 一 个 完 整 的 例 子， 它 使 用 了new 操 作 符 来 创 建 一 个University， 用 点 操 作 符 来 赋 值， 然 后 打 印 结 果。 class University { String name, city; public static void main(String args[]) { University u = new University( ); u.name = \"北 ?copy; 大 学\"; u.city = \"北 ?copy;\"; System.out.println(\"大 学：\" + u.name + \" 城 市：\" + u.city); } }

运 行 这 个 程 序 后， 就 会 得 到 下 面 的 结 果。 C:\\>java University 大 学： 北 ?copy; 大 学 城 市： 北 ?copy;

5.5 成 员 函 数 定 义

成 员 函 数， 是 类 的 功 能 接 口， 是 类 定 义 里 的 一 个 子 程 序， 在 类 的 定 义 里 和 实 例 变 量 处 于 同 一 级 别。 你 必 须 通 过 一 个 类 的 实 例 来 调 用 成 员 函 数。 成 员 函 数 可 以 不 用 点 操 作 符 而 直 接 使 用 实 例 变 量。 成 员 函 数 带 有 输 入 参 数， 具 有
某 种 类 型 的 返 回 值。 成 员 函 数 定 义 的 一 般 形 式 如 下： type methodname ( formal-parameter-list ) { method-body; } 这 里type指 的 是 成 员 函 数 的 返 回 值 的 类 型， 如 果 没 有 返 回 值， 就 用 无 值（void?copy; 类 型。 methodname可 以 是 任 何 合 法 的 标 识 符， 但 不 能 与 当 前 的 类 名 相 同。formal-parameter-list是 用 逗 号 分 隔 的 类 型、 标 识 符 对 的 序 列。 如 果 没 有 参 数， 括 号 里 就 是 空 的。 还 是 用 我 们 的University的 例 子， 下 面 的 成 员 函 数 用 来 初 始 化 两 个 实 例 变 量。 成 员 函 数 是 在 类 的 大 括 号 ?reg; 内 定 义 的， 和 实 例 变 量 所 处 的 范 围 相 同。 class University { String name, city; void init(String a, String b) { name = a; city = b; } }

注 意， 我 们 这 里 直 接 给name和city赋 值， 而 没 有 象 以 前 那 样 用u1.name。 这 是 因 为 每 个 成 员 函 数 都 在 类 的 个 别 实 例 内 执 行。 我 们 创 建 的 类 的 实 例 具 有 它 自 己 的 实 例 变 量， 所 以 成 员 函 数 可 直 接 使 用 它 们。

5.6 成 员 函 数 调 用

可 以 用 点（.?copy; 操 作 符 来 调 用 一 个 类 的 实 例 的 成 员 函 数。 成 员 函 数 调 用 的 一 般 形 式 如 下： objectreference.methodname( parameter-list ); 这 里，objectreference是 指 向 某 个 对 象 的 变 量，methodname是objectreference所 属 类 的 一 个 成 员 函 数，parameter-list是 用 逗 号 分 隔 的 变 量 或 表 达 式 的 序 列， 它 们 要 与 该 成 员 函 数 的 定 义 的 参 数 个 数 及 类 型 匹 配。 在 这 个 例 子 里， 我 们 可 以 对 任 何University对 象 调 用 成 员 函 数init来 给name和city赋 值。 下 面 的 程 序 段 说 明 了 怎 样 完 成 这 个 工 作。 University u = new University( ); u.init(\"北 ?copy; 大 学\", \"北 ?copy;\"); 这 个 例 子 创 建 了University的 一 个 实 例， 存 放 在u中。 通 过 点 操 作 符 来 调 用 这 个 实 例 的init 成 员 函 数， 把\"北 ?copy; 大 学\"和\"北 ?copy;\"分 别 传 递 给 参 数a和b。 在init成 员 函 数 内 部，name和city 直 接 指 向u所 指 向 的 对 象 的 实 例 变 量。 把name 赋 值 为\"北 ?copy; 大 学\"，city赋 值 为\"北 ?copy;\"， 然 后 返 回。 在 这 个 例 子 里，init被 定 义 为 无 值（void?copy; 返 回 类 型。 在 进 行 这 个 成 员 函 数 调 用 后，u指 向 这 个name值 和city值 改 变 了 的University对 象。

5.7 this Java有 一 个 特 殊 的 实 例 值 叫this， 它 用 来 在 一 个 成 员 函 数 内 部 指 向 当 前 的 对 象。 在 前 面 的 例 子 里， 我 们 调 用u.init， 一 ?copy; 进 入init成 员 函 数 内 部，this就 会 指 向u所 指 向 的 对 象。 在Java里， 在 同 一 个 范 围 定 义 两 个 相 同 名 字 的 局 部 变
量 是 不 可 以 的。 有 趣 的 是， 局 部 变 量、 成 员 函 数 的 参 数 可 以 和 实 例 变 量 的 名 字 相 同。 前 面 我 们 没 有 用name和city作 为 成 员 函 数init的 参 数 名 字， 因 为 这 样 它 们 在 成 员 函 数 的 范 围 里 就 把 实 例 变 量name和city隐 藏 了， 即name指 向 参 数name， 隐 藏 了 实 例 变 量name。this让 我 们 可 以 直 接 指 向 对 象 本 身。 下 面 是 另 一 个 版 本 的 init， 用name和city作 为 参 数 名 字， 用this来 接 收 当 前 对 象 的 实 例 变 量。
void init(String name, String city) { this.name = name; this.city = city; } 下 面 是 带 有 新 的init初 始 成 员 函 数 的TwoUniversity例 子。 class University { String name, city; void init(String name, String city) { this.name = name; this.city = city; } } class TwoUniversityInit { public static void main(String args[]) { University u1 = new University( ); University u2 = new University( ); u1.init(\"北 ?copy; 大 学\", \"北 ?copy;\"); u2.init(\"清 华 大 学\", \"北 ?copy;\"); System.out.println(\"大 学：\" + u1.name + \" 城 市：\" + u1.city); system.out.println(\"大 学：\" + u2.name + \" 城 市：\" + u2.city); } }

5.8 构 造 函 数（Constructor)

每 创 建 一 个 类 的 实 例 都 去 初 始 化 它 的 所 有 变 量 是 乏 味 的。 如 果 一 个 对 象 在 被 创 建 时 就 完 成 了 所 有 的 初 始 工 作， 将 是 简 单 的 和 简 洁 的。 因 此，Java在 类 里 提 ?copy 了 一 个 特 殊 的 成 员 函 数， 叫 做 构 造 函 数（Constructor?copy;。 一 个 构 造 函 数 是 对 象 被 创 建 时 初 始 对 象 的 成 员 函 数。 它 具 有 和 它 所 在 的 类 完 全 一 样 的 名 字。 一 ?copy; 定 义 好 一 个 构 造 函 数， 创 建 对 象 时 就 会 自 动 调 用 它。 构 造 函 数 没 有 返 回 类 型， 即 使 是void类 型 也 没 有。 这 是 因 为 一 个 类 的 构 造 函 数 的 返 回 值 的 类 型 就 是 这 个 类 本 身。 构 造 函 数 的 任 务 是 初 始 一 个 对 象 的 内 部 状 态， 所 以 用new操 作 符 创 建 一 个 实 例 后， 立 刻 就 会 得 到 一 个 清 楚、 可 用 的 对 象。 下 面 这 个 例 子 里， 用 构 造 函 数 取 代 了 成 员 函 数init。
class University { String name, city; University(String name, String city)
{ this.name = name; this.city = city; } } class UniversityCreate { public static void main(String args[]) { University u = new University(\"北 ?copy; 大 学\", \"北 ?copy;\"); System.out.println(\"大 学：\" + u.name + \" 城 市：\" + u.city); } } new语 句 中 类 名 后 的 参 数 是 传 给 构 造 函 数 的。

5.9 成 员 函 数 重 载

对 于 几 个 意 义 相 近 的 成 员 函 数， 有 时 使 用 相 同 的 名 字 便 于 理 解。 因 此，Java语 言 实 现 了 成 员 函 数 重 载， 即 可 以 创 建 几 个 名 字 相 同、 参 数 不 同 的 成 员 函 数。 成 员 函 数 重 载 提 ?copy; 了Java的 多 态 行 为。 下 面 的 例 子 用 到 了 重 载。 class University { String name, city; University(String name, String city) { this.name = name; this.city = city; } University( ) { name = \"北 ?copy; 大 学\"; city = \"北 ?copy;\"; } }
class UniversityCreateAlt { public static void main(String args[]) { University u = new University( ); System.out.println(\"大 学：\" + u.name + \" 城 市：\" + u.city); } }

这 个 例 子 创 建 了 一 个University对 象， 调 用 了 第 二 个 构 造 函 数。 下 面 是 它 的 运 行 结 果。
C:\\>java UniversityCreateAlt 大 学： 北 ?copy; 大 学 城 市： 北 ?copy; 一 个 构 造 函 数 可 以 调 用 另 一 个 构 造 函 数 来 创 建 实 例。 例 如：
class University { String name, city; University(String name, String city)
{ this.name = name; this.city = city; } University( ) { this(\"北 ?copy; 大 学\", \"北 ?copy;\"); } }

第 二 个 构 造 函 数 调 用 了 第 一 个 构 造 函 数 来 完 成 实 例 的 初 始 化。 你 也 可 以 用 重 载 来 创 建 一 般 的 成 员 函 数。 下 面 这 个 例 子 里 有University类 的 两 个 版 本 的 samecity成 员 函 数。samecity判 断 一 个 大 学 是 否 在 一 个 城 市 里 或 一 个 大 学 和 另 一 个 大 学 是 否 在 同 一 个 城 市 里。 一 个 成 员 函 数 用city作 参 数， 另 一 个 用University对 象 作 参 数。
class University { String name, city; University(String name, String city)
{ this.name = name; this.city = city; } boolean samecity(String city) { if
(city.equals(this.city)) return true; else return false; } boolean
samecity(University u) { return samecity(u.city); } }
class UniversityCity { public static void main(String args[]) { String
city = \"上海\"; University u1 = new University(\"北 ?copy; 大 学\", \"
北 ?copy;\"); University u2 = new University(\"清 华 大 学\", \"北 ?copy;\");
System.out.println(\"u1 = \" + u1.name + \", \" + u1.city);
System.out.println(\"u2 = \" + u2.name + \", \" + u2.city);
System.out.println(\"city = \" + city);
System.out.println(\"u1.samecity(u2) = \" + u1.samecity(u2));
System.out.println(\"u1.samecity(city) = \" + u1.samecity(city)); } }
下 面 是 该 程 序 的 运 行 结 果。
C:\\>java UniversityCity u1 = 北 ?copy; 大 学, 北 ?copy; u2 = 清 华 大 学, 北 ?copy; city = 上海 u1.samecity(u2) = true u1.samecity(city) = false

5.10 继 承

第 二 个 基 本 的 面 向 对 象 机 制 是 继 承。 继 承 是 关 于 有 层 次 关 系 的 类 ?reg; 间 的 概 念。 一 个 类 的 后 代 可 以 继 承 它 的 祖 先 的 所 有 变 量 和 成 员 函 数， 就 象 创 建 自 己 的 一 样。 一 个 类 的 直 接 父 亲 叫 做 它 的 超 类（superclass?copy;。 一 ?copy; 你 创 建 了 一 个 象University这 样 的 类， 创 建 它 的 子 类 是 很 简 单 的。 一 个 类 的 子 类 是 它 的 继 承 了 实 例 变 量 和 成 员 函 数 的 特 殊 的 版 本。 在 这 个 例 子 里， 我 们 把University类 派 生 为 含 有 叫 做country的 第 三 个 元 素 的 子 类。

class UniversityWorld extends University { String country; UniversityWorld(String name, String city, String country) { this.name = name; this.city = city; this.country = country; } UniversityWorld( ) { this(\"北 ?copy; 大 学\", \"北 ?copy;\", \"中 国\"); } }

关 键 词extends用 来 表 示 我 们 要 创 建University的 子 类。name和city不 需 再 在UniversityWorld 中 进 行 声 明， 因 为 它 们 是 从University中 继 承 的。Java允 许 在UniversityWorld中 声 明 变 量name 和city， 但 这 会 隐 藏University中 的name和city， 是 与 使 用 子 类 的 目 的 相 矛 盾 的， 应 当 避 免。 在 UniversityWorld的 实 例 中name、city和country的 地 位 是 一 样 的。

5.11 super 在UniversityWorld的 例 子 里， 有 一 段 代 码 和 它 的 超 类University的 重 复， 这 段 代 码 是 初 始 化 name和city的， this.name = name; this.city = city; 就 象 在University例 子 中 用this指 向 第 一 个 构 造 函 数 一 样， 在Java里 有 另 一 个 变 量 叫 做 super， 它 直 接 指 向 超 类 的 构 造 函 数。 下 面 这 个 例 子 用super来 初 始 化 变 量name和city， 然 后 打 印 出 这 个 对 象 的 内 容。
class UniversityWorld extends University { String country; UniversityWorld(String name, String city, String country) { super(name, city); // 调 用 了 构 造 函 数University(name, city) this.country = country; } public static void main(String args[]) { UniversityWorld u = new UniversityWorld(\"北 ?copy; 大 学\", \"北 ?copy;\", \"中 国\"); System.out.println(\"大 学：\" + u.name + \" 城 市：\" + u.city + \" 国 家：\" + u.country); } }
下 面 是 运 行 结 果。 C:\\>java UniversityWorld 大 学： 北 ?copy; 大 学 城 市： 北 ?copy; 国
家： 中 国

5.12 成 员 函 数 的 覆 盖

这 个University的 新 的 子 类 继 承 了 它 的 超 类 的 成 员 函 数samecity。 但 这 个 成 员 函 数samecity 判 断 的 是 两 个 城 市 的 名 字， 这 是 不 够 的， 因 为 有 可 能 两 个 两 个 名 字 一 样 的 城 市 属 于 不 同 的 国 家， 我 们 要 用 同 时 判 断 城 市 和 国 家 的 成 员 函 数 来 覆 盖 它。 下 面 就 是 实 现 覆 盖 的 例 子。
class University { String name, city; University(String name, String city)
{ this.name = name; this.city = city; } boolean samecity(String city) { if
(city.equals(this.city)) return true; else return false; } boolean
samecity(University u) { return samecity( u.city); } }
class UniversityWorld extends University { String country;
UniversityWorld(String name, String city, String country) { super(name,
city); this.country = country; } boolean samecity(String city, String
country) { if (city.equals(u.city) && country.equals(u.country)) return
true; else return false; } boolean samecity(UniversityWorld other)
{ return distance(other.city, other.country); } }
class UniversityWorldCity { public static void main(String args[])
{ String city = \"上海\"; String country = \"中 国\"; UniversityWorld u1 =
new UniversityWorld(\"北 ?copy; 大 学\", \"北 ?copy;\", \"中 国\");
UniversityWorld u2 = new UniversityWorld(\"清 华 大 学\", \"北 ?copy;\", \"
中 国\"); System.out.println(\"u1 = \" + u1.name + \", \" + u1.city + \", \" +
u1.country); System.out.println(\"u2 = \" + u2.name + \", \" + u2.city+ \",
\" + u2.country); System.out.println(\"city = \" + city + \", country = \" +
country); System.out.println(\"u1.samecity(u2) = \" + u1.samecity(u2));
System.out.println(\"u1.samecity(city, country) = \" + u1.samecity(city,
country)); } }
下 面 是 输 出 结 果。
C:\\>java UniversityWorldCity u1 = 北 ?copy; 大 学, 北 ?copy;, 中 国 u2= 清 华 大 学, 北 ?copy;, 中 国 city = 上海, country = 中 国 u1.samecity(u2) = true u1.samecity(city, country) = false

5.13 动 态 成 员 函 数 发 送

当 你 用 点 操 作 符 调 用 一 个 对 象 实 例 的 成 员 函 数 时， 对 象 实 例 所 属 的 类 在 编 译 时 要 被 检 查， 以 确 保 调 用 的 成 员 函 数 在 该 类 中 是 存 在 的。 在 运 行 时， 对 象 实 例 可 以 指 向 所 声 明 类 型 的 子 类 的 实 例。 在 这 ?copy; 情 况 下， 如 果 子 类 覆 盖 了 要 调 用 的 成 员 函 数，Java就 用 实 例 来 决 定 调 用 哪 一 个 成 员 函 数。 如 下 面 的 例 子， 两 个 类 是 子 类 和 超 类 的 关 系， 子 类 覆 盖 了 超 类 的 成 员 函 数。

class A { void callme( ) { System.out.println(\"在A的callme成 员 函 数 里\"); } }
class B extends A { void callme( ) { System.out.println(\"在B的callme成 员 函 数 里\"); } }
class Dispatch { public static void main(String args[]) { A a = new B( ); a.callme( ); } }
有 趣 的 是， 在 成 员 函 数main里， 我 们 把 变 量a声 明 为 类 型A， 然 后 把 类B的 一 个 实 例 存 放 到 它 上 面。 我 们 在a上 调 用 成 员 函 数callme，Java编 译 器 确 定 在 类A确 实 有 成 员 函 数callme， 但 是 在 运 行 时， 由 于a事 实 上 是B的 实 例， 所 以 调 用B的callme， 而 不 调 用A的。 下 面 是 运 行 结 果： C:\\>java Dispatch 在B的callme成 员 函 数 里

5.14 final

在 缺 省 情 况 下， 所 有 的 成 员 函 数 和 实 例 变 量 都 可 以 被 覆 盖。 如 果 你 希 望 你 的 变 量 或 成 员 函 数 不 再 被 子 类 覆 盖， 可 以 把 它 们 声 明 为final。 这 意 味 着 将 来 的 实 例 都 依 赖 这 个 定 义。 例 如： final int FILE_NEW = 1; final int FILE_OPEN = 2; final int FILE_SAVE = 3; fianl int FILE_SAVEAS = 4; final int FILE_QUIT = 5; final变 量 用 大 写 标 识 符 是 一 个 一 般 的 约 定。

5.15 静 态

如 果 你 想 要 创 建 一 个 可 以 在 实 例 的 外 部 调 用 的 成 员 函 数， 那 么 你 只 需 声 明 它 为 静 态 的 （static?copy;， 它 就 会 正 常 运 行。 静 态 成 员 函 数 只 能 直 接 调 用 其 他 静 态 成 员 函 数， 而 不 能 以 任 何 方 式 使 用this或super。 你 也 可 以 把 变 量 声 明 为 静 态 的。 如 果 你 想 初 始 化 一 个 静 态 变 量， 你 可 以 用 static声 明 一 个 恰 好 在 类 调 用 时 执 行 一 次 的 程 序 块。 下 面 的 例 子 是 一 个 带 有 一 个 静 态 成 员 函 数， 几 个 静 态 变
量， 和 一 个 静 态 初 始 块 的 类。
class Static { static int a = 3; static int b; static void method(int x){ System.out.println(\"x = \" + x); System.out.println(\"a = \" + a); System.out.println(\"b = \" + b); } static { System.out.println(\"静 态 初 始 块\"); b = a * 4; } public static void main(String args[]) { method(42); } } 这 个 类 被 调 用， 所 有 的 静 态 变 量 都 被 初 始 化，a被 赋 为3， 然 后 运 行static块， 这 将 打 印 出 一 段 消 息， 并 且 把b赋 为a*4， 即12。 然 后 解 释 器 调 用main成 员 函 数， 它 调 用 了 成 员 函 数 method， 参 数x为42。 这 三 个println语 句 打 印 了 两 个 静 态 变 量a、b和 局 部 变 量x。 下 面 是 运 行 结 果： C:\\>java Static 静 态 初 始 块 x = 42 a = 3 b = 12 一 个 静 态 成 员 函 数 可 以 通 过 它 所 属 的 类 名 来 调 用。 象 调 用 实 例 变 量 一 样， 你 可 以 用 点 操 作 符 通 过 类 名 来 调 用 静 态 成 员 函 数 和 静 态 变 量。Java就 是 这 样 实 现 了 全 局 函 数 和 全 局 变 量。 下 面 的 例 子 里， 我 们 创 建 了 带 有 一 个 静 态 成 员 函 数 和 两 个 静 态 变 量 的 类。 第 二 个 类 可 以 通 过 名 字 直 接 来 调 用 第 一 个 类 的 静 态 成 员 函 数 和 静 态 变 量。
class staticClass { static int a = 42; static int b = 99; static void
callme( ) { System.out.println(\"a = \" + a); } }
class StaticByName { public static void main(String args[])
{ StaticClass.callme( ); System.out.println(\"b = \" + staticClass.b); } }
下 面 是 运 行 结 果： C:\\>java staticByName a = 42 b = 99

5.16 抽 象

有 时 你 需 要 定 义 一 个 给 出 抽 象 结 构、 但 不 给 出 每 个 成 员 函 数 的 完 整 实 现 的 类。 如 果 某 个 成 员 函 数 没 有 完 整 实 现， 必 须 要 由 子 类 来 覆 盖， 你 可 把 它 声 明 为 抽 象（abstract?copy; 型。 含 有 抽 象 型 成 员 函 数 的 类 必 须 声 明 为 抽 象 的。 为 了 把 一 个 类 声 明 为 抽 象 的， 你 只 需 在 类 定 义 的class关 键 词 前 放 置 关 键 词abstract。 这 ?copy; 类 不 能 直 接 用new操 作 符 生 成 实 例， 因 为 它 们 的 完 整 实 现 还 没 有 定 义。 你 不 能 定 义 抽 象 的 构 造 函 数 或 抽 象 的 静 态 成 员 函 数。 抽 象 类 的 子 类 或 者 实 现 了 它 的 超 类 的 所 有 抽 象 的 成 员 函 数， 或 者 也 被 声 明 为 抽 象 的。 下 面 例 子 是 一 个 带 有 抽 象 成 员 函 数 的 类， 其 后 是 一 个 实 现 了 该 成 员 函 数 的 类。
abstract class A { abstract void callme( ) ; void metoo( ) { system.out.println(\"在A的metoo成 员 函 数 里\"); } } class B extends A { void callme( ) { System.out.println(\"在B的callme成 员 函 数 里\"); } } class Abstract { public static void main(String args[]) { A a = new B( );
a.callme( ); a.metoo( ); } }
下 面 是 运 行 结 果： C:\\>java Abstract 在B的callme成 员 函 数 里 在A的metoo成 员 函 数 里

本 章 小 结

1. 类 是Java语 言 面 向 对 象 编 程 的 基 本 元 素， 它 定 义 了 一 个 对 象 的 结 构 和 功 能。 2. Java通 过 在 类 定 义 的 大 括 号 里 声 明 变 量 来 把 数 据 封 装 在 一 个 类 里， 这 里 的 变 量 称 为 实 例 变 量。 3. 成 员 函 数， 是 类 的 功 能 接 口， 是 类 定 义 里 的 一 个 子 程 序， 在 类 的 定 义 里 和 实 例 变 量 处 于 同 一 级 别。


第 六 章 Java图 形 用 户 接 口

对 一 个 优 秀 的 应 用 程 序 来 说， 良 好 的 图 形 用 户 接 口 是 必 不 可 少 的。 缺 少 良 好 的 图 形 用 户 接 口， 将 会 给 用 户 理 解 和 使 用 应 用 程 序 带 来 很 多 不 便。 很 难 想 象 用 户 为 了 学 会 使 用 一 个 应 用 程 序， 去 记 一 大 堆 命 令。 Java提copy; 了 生 成 一 个 良 好 的 图 形 用 户 接 口 所 需 要 的 一copy; 基 本 元 件： 面 板（Panel copy;、 按 钮 （Button copy;、 标copy;（Label copy;、 画 板（Canvases copy;、 滚 动 条（Scrollbar copy;、 列 表 框（List copy;、 文 本 域（Text Field copy;、 文 本 区（Text Area copy;。

6.1 面 板

面 板 提copy; 了 建 立 应 用 程 序 的 空 间。 你 可 以 把 图 形 元 件（包 括 其 他 面 板 copy; 放 在 一 个 面 板 上。 Applet类 提copy; 了 一 个 基 本 的 面 板。

6.1.1 布 局 管 理

Java提copy; 了 几 种 布 局： 顺 序 布 局（Flow Layout copy;、 边 界 布 局（Border Layout copy; 和 网 格 布 局 （Grid Layout)

6.1.1.1 顺 序 布 局

顺 序 布 局（Flow Layout copy; 是 最 基 本 的 一 种 布 局， 面 板 的 缺 省 布 局 就 是 顺
            序 布 局。 顺 序 布 局 指 的 是 把 图 形 元 件 一 个 接 一 个 地reg; 平 地 放 在 面 板 上。 下 面
            是 一 个 顺 序 布 局 的 例 子：
import java.awt.*; import java.applet.Applet;
public class myButtons extends Applet { Button button1, button2, button3;
public void init() { button1 = new Button(\"确 定\"); button2
            = new Button(\"打 开\"); button3 = new Button(\"关 闭\");
            add(button1); add(button2); add(button3); } }

6.1.1.2 边 界 布 局

边 界 布 局 包 括 五 个 区： 北 区、 南 区、 东 区、 西 区 和 中 区。 这 几 个 区 在 面 板 上 的 分
            布 规 律 是\" 上 北 下 南， 左 西 右 东\"。 下 面 是 一 个 边 界 布 局 的 例 子：
import java.awt.*; import java.applet.Applet;
public class buttonDir extends Applet { Button buttonN, buttonS, buttonW,
            buttonE, buttonC;
public void init() { setLayout(new BorderLayout()); buttonN = new
            Button(\" reg;\"); buttonS = new Button(\"火\");
            buttonE = new Button(\"木\"); buttonW = new Button(\"金\");
            buttonC = new Button(\"土\"); add(\"North\", buttonN);
            add(\"South\", buttonS); add(\"East\", buttonE);
            add(\"West\", buttonW); add(\"Center\", buttonC);
            } }

6.1.1.3 网 格 布 局

网 格 布 局 把 面 板 分 成 一 个 个 的 网 格， 你 可 以 给 出 网 格 的 行 数 和 列 数。 下 面 是 一
            个 网 格 布 局 的 例 子：
import java.awt.*; import java.applet.Applet;
public class buttonGrid extends Applet { Button button1, button2,
            button3, button4, button5, button6, button7, button8;
public void init() { setLayout(new GridLayout(4,2)); button1 = new
            Button(\"乾\"); button2 = new Button(\"坤\"); button3
            = new Button(\"艮\");
button4 = new Button(\"震\"); button5 = new Button(\"坎\");
            button6 = new Button(\"离\"); button7 = new Button(\"巽\");
            button8 = new Button(\"兑\");
add(button1); add(button2); add(button3); add(button4); add(button5);
            add(button6); add(button7); add(button8); } }

6.2 按 钮

6.2.1 按 钮 事 件

用 户 点 一 下 按 钮， 就 会 有 一 个 按 钮 事 件 发 生。 你 可 以 通 过 覆 盖 一 个applet的action成 员 函 数 来 捕 捉 按 钮 事 件。 public boolean action (Event e, Object o) { if (e.target instanceof Button) { system.out.println ((string) o); } else { System.out.println (\"Non-button event\"); } return true; }

6.2.2 按 钮 类 型

Java提copy; 了 标 准 的 按 压 式 按 钮, 同 时 也 提copy; 了 选 择 式 按 钮 和 标 记 式 按 钮。

6.2.2.1 选 择 式 按 钮

选 择 式 按 钮 提copy; 了 从 几 个 选 项 中 选 一 个 选 项 的 功 能。 下 面 是 从 几 个 市 中 选
            一 个 市 的 例 子， 市 名 放 在 选 择 式 按 钮 中：
CityChooser = new Choice();
CityChooser.addItem(\"北copy;\"); CityChooser.addItem(\"上海\");
CityChooser.addItem(\"天 津\");
add(CityChooser);

6.2.2.2 标 记 式 按 钮

标 记 式 按 钮 的 状 态 作 为 标 记 框 事 件 的 对 象 参 数 返 回。 下 面 是 一 个 标 记 式 按 钮 的 例 子：
Checkbox fillStyleButton; fillStyleButton = new Checkbox(\"Solid\");
public boolean action(Event e, Object arg) { if (e.target instanceof
Checkbox) { System.out.println(\"Checkbox: \" + arg); } return true; }

6.2.2.3 按 键 式 按 钮

按 键 式 按 钮 是 一 组 按 钮， 用 户 可 以 选 中 其 中 一 个， 同 时 这 一 组 中 的 其 他 按 钮 将 被 关 闭。 下 面 是 一 个 按 键 式 按 钮 的 例 子： public class CheckBox extends Applet { CheckboxGroup cbg; public void init() { cbg = new CheckboxGroup(); add (new Checkbox(\"one \", cbg, true)); add (new Checkbox(\"two \", cbg,false)); add (new Checkbox(\"three\", cbg, false)); } }

6.2.3 自 包 含 按 钮

Java语 言 的 面 向 对 象 特 性 使 我 们 能 够 创 建 完 全 自 包 含 的 按 钮。 在 自 包 含 按 钮
            里， 你 可 以 在copy; 展 按 钮 类 里 建 立 事 件 控 制 函 数。 下 面 是 一 个 自 包 含 按 钮 的 例
            子：
import java.awt.*; import java.applet.Applet;
class okButton extends Button {
public okButton() { setLabel(\"Ok\"); }
public boolean action(Event e, Object arg)
{ System.out.println(\"OKButton\"); return true; } }
public class buttontest extends Applet { okButton myOkButton;
public void init() { myOkButton = new okButton(); add(myOkButton);
            } }

6.3 标copy;

标copy; 是 一 种 放 到 面 板 上 的 静 止 的 正 文。 下 面 是 一 个 标copy; 的 例 子： import java.awt.*; import java.applet.Applet; public class label extends Applet { public void init() { setLayout(new FlowLayout(FlowLayout.CENTER, 10, 10)); Label label1 = new Label(\"你 好!\"); Label label2 = new Label(\"另 一 个 标copy;\"); add(label1); add(label2); } }

6.4 列 表 框

列 表 框 使 用 户 易 于 操 作 大 量 的 选 项。 创 建 列 表 框 的 方 法 和Choice button有copy; 相 似。 列 表 框 的 所 有 条 目 都 是 可 见 的， 如 果 选 项 很 多， 超 出 了 列 表 框 可 见 区 的 范 围， 则 列 表 框 的 旁 边 将 会 有 一 个 滚 动 条。 首 先， 创 建 列 表 框： List l = new List(4, false); 这 个 成 员 函 数 创 建 了 一 个 显 示4行 的 列 表 框。 第 二 个 参 数\"false\"表 示 这 个 列 表 框 是 单 选 的， 如 果 是\"true \"， 则 表 示 是 多 选 的。 下 面 增 加 列 表 框 的 选 项： l.addItem(\"北copy; 大 学\"); l.addItem(\"清 华 大 学\"); l.addItem(\"吉林 大 学\"); l.addItem(\"复copy; 大 学\"); l.addItem(\"南 开 大 学\"); l.addItem(\"天 津 大 学\"); l.addItem(\"南copy; 大 学\"); add(l);

6.4.1 在 列 表 框 中 进 行 选 择

可 以 用 成 员 函 数getSelectedItem()或getSelectedItems()来 接 收 在 列 表 框 中 被 选 的 选 项。 在 单 选 列 表 框 里，\" 双 击\" 一 个 选 项 就 可 以 触 发 一 个 可 被action()成 员 函 数 捕 捉 到 的 事 件。 public boolean action(Event e, Object arg) { . . . if (e.target instanceof List) { System.out.println(\"List entry:\" + arg); } . . . }

6.4.2 多 选 列 表 框

对 于 多 选 列 表 框， 要 使 你 的 选 择 产 生 作 用， 需 要 使 用 其 他 的 外 部 事 件。 例 如， 你 可 以 使 用 按 钮 事 件： public boolean action(Event e, Object arg) { . . . if (e.target instanceof Button) { . . . if (\"Ok\".equals(arg)) { string[] selected ; selected = l.getSelectedItems( ); for (int I = 0; I< selected.length; I++) {System.out.println(selected[i]); } } } }

6.5 文 本 域

文 本 域 一 般 用 来 让 用 户 输 入 象 姓 名、 信 用 卡 号 这 样 的 信 息， 它 是 一 个 能 够 接 收 用 户 的 键 盘 输 入 的 小 块 区 域。

6.5.1 创 建 文 本 域

在 创 建 文 本 域 时， 有 四 种 类 型copy; 你 选 择： 空 的、 空 的 并 且 具 有 指 定 长 度、 带 有 初 始 文 本 内 容 的 和 带 有 初 始 文 本 内 容 并 具 有 指 定 长 度 的。 下 面 是 生 成 这 四 种 文 本 域 的 代 码： TextField tf1, tf2, tf3, tf4; // 空 的 文 本 域 tf1 = new TextField() ; // 长 度 为20的 空 的 文 本 域 tf2 = new TextField(20) ; // 带 有 初 始 文 本 内 容 的 文 本 域 tf3
= new TextField(\"你 好\") ; // 带 有 初 始 文 本 内 容 并 具 有 指 定 长 度 的 文 本 域 tf4 = new TextField(\"你 好\", 30) ; add(tf1) ; add(tf2) ; add(tf3) ; add(tf4) ;

6.5.2 文 本 域 事 件

当 用 户 在 文 本 域 里 敲\" 回 车\" 键 时， 就 产 生 了 一 个 文 本 域 事 件。 象 其 他 事 件 一 样， 你 可 以 以 在 成 员 函 数action()中 捕 捉 到 这 个 事 件。
public boolean action(Event e, Object arg) { . . . if (e.target instanceof TextField) { System.out.println(\"TextField: \"+arg); } . . . }

6.6 文 本 区

文 本 区 可 以 显 示 大 段 的 文 本。

6.6.1 创 建 文 本 区

与 文 本 域 类 似， 创 建 文 本 区 时 也 有 四 种 类 型copy; 选 择， 但 如 果 指 定 文 本 区 的 大 小， 必 须 同 时 指 定 行 数 和 列 数。 TextArea ta1, ta2; // 一 个 空 的 文 本 区 ta1 = new TextArea(); // 一 个 带 有 初 始 内 容、 大 小 为5 x 40 的 文 本 区 ta2 = new TextArea(\"你 好!\", 5, 40);
可 以 用 成 员 函 数setEditable()来 决 定 用 户 是 否 可 对 文 本 区 的 内 容 进 行 编 辑。 // 使 文 本 区 为 只 读 的 ta2.setEditable(false)

6.6.2 接 收 文 本 区 的 内 容

可 以 用 成 员 函 数getText()来 获 得 文 本 区 的 当 前 内 容。 例 如： System.out.println(ta1.getText()); 文 本 区 本 身 不 产 生 自 己 的 事 件。 但 你 可 以 用 外 部 事 件 来 接 收 文 本 区 的 内 容： public boolean action(Event e, Object o) { if (e.target instanceof Button) { if (\"send\".equals(o)) { String textToSend = ta1.getText ();
System.out.println(\"sending: \" + textTosend);
mySendFunction(textToSend); } } else { . . . } }

6.7 画 板

画 板 能 够 捕 捉 到copy; 露 事 件、 鼠 标 事 件 和 其 他 类 似 的 事 件。 基 本 的 画 板 类 不 处 理 这copy; 事 件， 但 你 可 以copy; 展 它 来 创 建 有 你 所 需 功 能 的 画 板 类。

6.7.1 创 建 画 板

import java.awt.*; import java.applet.Applet;
public class superGUI extends Applet { . . . myCanvas doodle; . . . public
void init() { . . . // 建 立 我 们 的 画 板 doodle = new myCanvas();
doodle.reshape(0, 0, 100, 100); leftPanel.add(\"Center\",doodle); . . . } }
class myCanvas extends Canvas { public void paint(Graphics g)
{ g.drawRect(0, 0, 99, 99); g.drawString(\"Canvas\", 15, 40); } }

6.7.2 画 板 事 件

你 可 以 覆 盖 一 般 的 事 件 处 理 成 员 函 数。 下 面 是 一 个 包 含 了mouseDown事 件 处 理 的 例 子： import java.awt.*; import java.applet.Applet;
public class canvas extends Applet {
Button b1;
public void init() { // Set our layout as a Border style setLayout(new
BorderLayout(15, 15)); b1 = new Button(\"Test\"); myCanvas c1 = new
myCanvas(100, 100); // add the canvas and the button to the applet
add(\"Center\", c1); add(\"South\", b1); }
public boolean action(Event e, Object arg) { System.out.println(\"Event:
\" + arg); return true; }
public boolean mouseDown(Event e, int x, int y)
{ System.out.println(\"Mouse works: (\" + x + \",\" + y + \")\"); return true; } }
class myCanvas extends Canvas { private int width; private int height;
public myCanvas(int w, int h) { width = w; height = h; reshape(0, 0, w,
h); }
public void paint(Graphics g) { g.setColor(Color.blue); g.fillRect(0, 0,
width, height); }
public boolean mouseDown(Event e, int x, int y) { if (( x < width) && (y


第 七 章 多 线 程

7.1 多 线 程 的 概 念

多 线 程 编 程 的 含 义 是 你 可 将 程 序 任 务 分 成 几 个 并 行 的 子 任 务。 特 别 是 在 网 络 编 程 中， 你 会 发 现 很 多 功 能 是 可 以 并 发 执 行 的。 比 如 网 络 传 输 速 度 较 慢， 用 户 输 入 速 度 较 慢， 你 可 以 用 两 个 独 立 的 线 程 去 完 成 这 ?copy; 功 能， 而 不 影 响 正 常 的 显 示 或 其 他 功 能。 多 线 程 是 与 单 线 程 比 较 而 言 的， 普 通 的WINDOWS采 用 单 线 程 程 序 结 构， 其 工 作 原 理 是： 主 程 序 有 一 个 消 息 循 环， 不 断 从 消 息 队 列 中 读 入 消 息 来 决 定 下 一 步 所 要 干 的 事 情， 一 般 是 一 个 子 函 数， 只 有 等 这 个 子 函 数 执 行 完 返 回 后， 主 程 序 才 能 接 收 另 外 的 消 息 来 执 行。 比 如 子 函 数 功 能 是 在 读 一 个 网 络 数 据， 或 读 一 个 文 件， 只 有 等 读 完 这 ?copy; 数 据 或 文 件 才 能 接 收 下 一 个 消 息。 在 执 行 这 个 子 函 数 过 程 中 你 什 么 也 不 能 干。 但 往 往 读 网 络 数 据 和 等 待 用 户 输 入 有 很 多 时 间 处 于 等 待 状 态， 多 线 程 利 用 这 个 特 点 将 任 务 分 成 多 个 并 发 任 务 后， 就 可 以 解 决 这 个 问 题。

7.1.1 Java线 程 的 模 型

Java的 设 计 思 想 是 建 立 在 当 前 大 多 数 操 作 系 统 都 实 现 了 线 程 调 度。Java虚 拟 机 的 很 多 任 务 都 依 赖 线 程 调 度， 而 且 所 有 的 类 库 都 是 为 多 线 程 设 计 的。 实 时 上，Java支 持Macintosh和Ms-dos 的 平 台 ?reg; 所 以 迟 迟 未 出 来 就 是 因 为 这 两 个 平 台 都 不 支 持 多 线 程。Java利 用 多 线 程 实 现 了 整 个 执 行 环 境 是 异 步 的。 在Java程 序 里 没 有 主 消 息 循 环。 如 果 一 个 线 程 等 待 读 取 网 络 数 据， 它 可 以 运 行 但 不 停 止 系 统 的 其 他 线 程 执 行。 用 于 处 理 用 户 输 入 的 线 程 大 多 时 间 是 等 待 用 户 敲 键 盘 或 击 鼠 标。 你 还 可 以 使 动 画 的 每 一 帧 ?reg; 间 停 顿 一 秒 而 并 不 使 系 统 暂 停。 一 ?copy; 线 程 启 动 后， 它 可 以 被 挂 起， 暂 时 不 让 它 执 行。 挂 起 的 线 程 可 以 重 新 恢 复 执 行。 任 何 时 间 线 程 都 可 以 被 停 止， 被 停 止 的 线 程 就 不 能 再 重 新 启 动。 Java语 言 里， 线 程 表 现 为 线 程 类， 线 程 类 封 装 了 所 有 需 要 的 线 程 操 作 控 制。 在 你 心 里， 必 须 很 清 晰 地 区 分 开 线 程 对 象 和 运 行 线 程， 你 可 以 将 线 程 对 象 看 作 是 运 行 线 程 的 控 制 面 板。 在 线 程 对 象 里 有 很 多 函 数 来 控 制 一 个 线 程 是 否 运 行， 睡 眠， 挂 起 或 停 止。 线 程 类 是 控 制 线 程 行 为 的 唯 一 的 手 段。 一 ?copy; 一 个Java程 序 启 动 后， 就 已 经 有 一 个 线 程 在 运 行。 你 可 通 过 调 用Thread.currentThread 函 数 来 查 看 当 前 运 行 的 是 哪 一 个 线 程。
你 得 到 一 个 线 程 的 控 制 柄， 你 就 可 以 作 很 有 趣 的 事 情， 即 使 单 线 程 也 一 样。 下 面 这 个 例 子 让 你 知 道 怎 样 操 纵 当 前 线 程。 Filename:testthread
class testthread { public static void main(String args[]) { Thread t
=Thread.currentThread(); t.setName(\"This Thread is running\");
System.out.println(\"The running thread:\" + t); try { for (int i=0;i<5;i++)
{ System.out.println(\"Sleep time \"+i); Thread.sleep(1000); }
} catch (InterruptedException e) {System.out.println(\"thread has wrong\"); }
} }
执 行 结 果：java testthread The running thread:Thread[This Thread is running,5,main] Sleep time 0 Sleep time 1 Sleep time 2 Sleep time 3 Sleep time 4

7.1.2 启 动 接 口

一 个 线 程 并 不 激 动 人 心， 多 个 线 程 才 有 实 际 意 义。 我 们 怎 样 创 建 更 多 的 线 程 呢？ 我 们 需 要 创 建 线 程 类 的 另 一 个 实 例。 当 我 们 构 造 了 线 程 类 的 一 个 新 的 实 例， 我 们 必 须 告 诉 它 在 新 的 线 程 里 应 执 行 哪 一 段 程 序。 你 可 以 在 任 意 实 现 了 启动 接 口 的 对 象 上 启 动 一 个 线 程。 启 动 接 口 是 一 个 抽 象 接 口， 来 表 示 本 对 象 有 一 ?copy; 函 数 想 异 步 执 行。 要 实 现 启 动 接 口， 一 个 类 只 需 要 有 一 个 叫run的 函 数。 下 面 是 创 建 一 个 新 线 程 的 例 子：
Filename:twothread.java
class twothread implements Runnable { twothread() { Thread t1
=Thread.currentThread(); t1.setName(\"The first main thread\");
System.out.println(\"The running thread:\" + t1); Thread t2 = new
Thread(this,\"the second thread\"); System.out.println(\"creat another
thread\"); t2.start(); try { System.out.println(\"first thread will
sleep\"); Thread.sleep(3000); }catch (InterruptedException e)
{System.out.println(\"first thread has wrong\"); }
System.out.println(\"first thread exit\"); } public void run() { try { for
(int i=0;i<5;i++) { System.out.println(\"Sleep time for thread 2:\"+i);
Thread.sleep(1000); }
} catch (InterruptedException e) {System.out.println(\"thread has
wrong\"); }
System.out.println(\"second thread exit\"); } public static void
main(String args[]) { new twothread(); } }
执 行 结 果：java twothread
The running thread:Thread[The first main thread,5,main] creat another
thread first thread will sleep Sleep time for thread 2:0 Sleep time for
thread 2:1 Sleep time for thread 2:2 first thread exit Sleep time for
thread 2:3 Sleep time for thread 2:4 second thread exit
main线 程 用new Thread(this, \"the second thread\")创 建 了 一 个Thread对 象， 通 过 传 递 第 一 个 参 数 来 标 明 新 线 程 来 调 用this对 象 的run函 数。 然 后 我 们 调 用start函 数， 它 将 使 线 程 从run函 数 开 始 执 行。

7.1.3 同 步

因 为 多 线 程 给 你 提 ?copy; 了 程 序 的 异 步 执 行 的 功 能， 所 以 在 必 要 时 必 须 还 提 ?copy; 一 种 同 步 机 制。 例 如， 你 想 两 个 线 程 通 讯 并 共 享 一 个 复 杂 的 数 据 结 构， 你 需 要 一 种 机 制 让 他 们 相 互 牵 制 并 正 确 执 行。 为 这 个 目 的，Java用 一 种 叫 监 视 器(monitor)的 机 制 实 现 了 进 程 间 的 异 步 执 行。 可 以 将 监 视 器 看 作 是 一 个 很 小 的 盒 子， 它 只 能 容 纳 一 个 线 程。 一 ?copy; 一 个 线 程 进 入 一 个 监 视 器， 所 有 其 他 线 程 必 须 等 到 第 一 个 线 程 退 出 监 视 器 后 才 能 进 入。 这 ?copy; 监 视 器 可 以 设 计 成 保 护 共 享 的 数 据 不 被 多 个 线 程 同 时 操 作。 大 多 数 多 线 程 系 统 将 这 ?copy; 监 视 器 设 计 成 对 象，Java提 ?copy; 了 一 种 更 清 晰 的 解 决 方 案。 没 有Monitor类； 每 个 对 象 通 过 将 他 们 的 成 员 函 数 定 义 成synchronized来 定 义 自 己 的 显 式 监 视 器， 一 ?copy; 一 个 线 程 执 行 在 一 个synchronized函 数 里， 其 他 任 何 线 程 都 不 能 调 用 同 一 个 对 象 的
synchronized函 数。

7.1.4 消 息

你 的 程 序 被 分 成 几 个 逻 辑 线 程， 你 必 须 清 晰 的 知 道 这 ?copy; 线 程 ?reg; 间 应 怎 样 相 互 通 讯。Java 提 了wait和notify等 功 能 来 使 线 程 ?reg; 间 相 互 交 谈。 一 个 线 程 可 以 进 入 某 一 个 对 象 的synchronized 函 数 进 入 等 待 状 态， 直 到 其 他 线 程 显 式 地 将 它 唤 醒。 可 以 有 多 个 线 程 进 入 同 一 个 函 数 并 等 待 同 一 个 唤 醒 消 息。

7.2 Java线 程 例 子

7.2.1 显 式 定 义 线 程

在 我 们 的 单 线 程 应 用 程 序 里， 我 们 并 没 有 看 见 线 程， 因 为Java能 自 动 创 建 和 控 制 你 的 线 程。 如 果 你 使 用 了 理 解Java语 言 的 浏 览 器， 你 就 已 经 看 到 使 用 多 线 程 的Java程 序 了。 你 也 许 注 意 到 两 个 小 程 序 可 以 同 时 运 行， 或 在 你 移 动 滚 动 条 时 小 程 序 继 续 执 行。 这 并 不 是 表 明 小 程 序 是 多 线 程 的， 但 说 明 这 个 浏 览 器 是 多 线 程 的。 多 线 程 应 用 程 序(或applet)可 以 使 用 好 几 个 执 行 上 下 文 来 完 成 它 们 的 工
作。 多 线 程 利 用 了 很 多 任 务 包 含 单 独 的 可 分 离 的 子 任 务 的 特 点。 每 一 个 线 程 完 成 一 个 子 任 务。 但 是， 每 一 个 线 程 完 成 子 任 务 时 还 是 顺 序 执 行 的。 一 个 多 线 程 程 序 允 许 各 个 线 程尽快 执 行 完 它 们。 这 种 特 点 会 有 更 好 的 实 时 输 入 反 应。

7.2.2 多 线 程 例 子

下 面 这 个 例 子 创 建 了 三 个 单 独 的 线 程， 它 们 分 别 打 印 自 己 的\"Hello World\":
//Define our simple threads.They will pause for a short time //and then
print out their names and delay times class TestThread extends Thread
{ private String whoami; private int delay;
//Our constructor to store the name (whoami) //and time to sleep (delay)
public TestThread(String s, int d) { whoami = s; delay = d; }
//Run - the thread method similar to main() //When run is finished, the
thread dies. //Run is called from the start() method of Thread public void
run() { //Try to sleep for the specified time try { sleep(delay); }
catch(InterruptedException e) {} //Now print out our name
System.out.println(\"Hello World!\"+whoami+\"\"+delay); } } /** * Multimtest.
A simple multithread thest program */ public class multitest { public
static void main(String args[]) { TestThread t1,t2,t3; //Create our test
threads t1 = new TestThread(\"Thread1\",(int)(Math.readom()*2000)); t2 =
new TestThread(\"Thread2\",(int)(Math.readom()*2000)); t3 = new
TestThread(\"Thread3\",(int)(Math.readom()*2000));
//Start each of the threads t1.start(); t2.start(); t3.start(); } }

7.2.3 启 动 一 个 线 程

程 序 启 动 时 总 是 调 用main()函 数， 因 此main()是 我 们 创 建 和 启 动 线 程 的 地 方：
t1 = new TestThread(\"Thread1\",(int)(Math.readom()*2000));
这 一 行 创 建 了 一 个 新 的 线 程。 后 面 的 两 个 参 数 传 递 了 线 程 的 名 称 和 线 程 在 打 印 信 息 ?reg; 前 的 延 时 时 间。 因 为 我 们 直 接 控 制 线 程， 我 们 必 须 直 接 启 动 它： t1.start();

7.2.4 操 作 线 程

如 果 创 建 线 程 正 常，t1应 包 含 一 个 有 效 的 执 行 线 程。 我 们 在 线 程 的run()函 数 里 控 制 线 程。 一 ?copy; 我 们 进 入run()函 数， 我 们 便 可 执 行 里 面 的 任 何 程 序。run()好 象main()一 样。
run() 执 行 完， 这 个 线 程 也 就 结 束 了。 在 这 个 例 子 里， 我 们 试 着 延 迟 一 个 随 机 的 时 间（通 过 参 数 传 递?) sleep(delay);
sleep()函 数 只 是 简 单 地 告 诉 线 程 休 息 多 少 个 毫 秒 时 间。
如 果 你 想 推 迟 一 个 线 程 的 执 行， 你 应 使 用sleep()函 数。 当 线 程 睡 眠 是sleep()并 不 占 用 系 统 资 源。 其 它 线 程 可 继 续 工 作。 一 ?copy; 延 迟 时 间 完 毕， 它 将 打 印\"Hello World\"和 线 程 名 称 及 延 迟 时 间。

7.2.5 暂 停 一 个 线 程

我 们 经 常 需 要 挂 起 一 个 线 程 而 不 指 定 多 少 时 间。 例 如， 如 果 你 创 建 了 一 个 含 有 动 画 线 程 的 小 程 序。 也 许 你 让 用 户 暂 停 动 画 至 到 他 们 想 恢 复 为 止。 你 并 不 想 将 动 画 线 程 仍 调， 但 想 让 它 停 止。 象 这 种 类 似 的 线 程 你 可 用suspend()函 数 来 控 制： t1.suspend(); 这 个 函 数 并 不 永 久 地 停 止 了 线 程， 你 还 可 用resume()函 数 重 新 激 活 线 程： t1.resume();

7.2.6 停 止 一 个 线 程

线 程 的 最 后 一 个 控 制 是 停 止 函 数stop()。 我 们 用 它 来 停 止 线 程 的 执 行： t1.stop();
注 意： 这 并 没 有 消 灭 这 个 线 程， 但 它 停 止 了 线 程 的 执 行。 并 且 这 个 线 程 不 能 用t1.start()重 新 启 动。 在 我 们 的 例 子 里， 我 们 从 来 不 用 显 式 地 停 止 一 个 线 程。 我 们 只 简 单 地 让 它 执 行 完 而 已。 很 多 复 杂 的 线 程 例 子 将 需 要 我 们 控 制 每 一 个 线 程。 在 这 种 情 况 下 会 使 用 到stop()函 数。 如 果 需 要， 你 可 以 测 试 你 的 线 程 是 否 被 激 活。 一 个 线 程 已 经 启 动 而 且 没 有 停 止 被 认 为 是 激 活 的。 t1.isAlive() 如 果t1是 激 活 的， 这 个 函 数 将 返 回true.

7.2.7 动 画 例 子

下 面 是 一 个 包 含 动 画 线 程 的applet例 子：
import java.awt.*; import java.awt.image.ImageProducer; import
java.applet.Applet;
public class atest3 extends Applet implements Runnable { Image images[];
MediaTracker tracker; int index = 0; Thread animator;
int maxWidth,maxHeight; //Our off-screen components for double buffering.
Image offScrImage; Graphics offScrGC;
//Can we paint yes? boolean loaded = false;
//Initialize the applet. Set our size and load the images public void
            init()
[ //Set up our image monitor tracker = new MediaTracker(this);
//Set the size and width of our applet maxWidth = 100; maxHeight =100;
images = new Image[10]; //Set up the double-buffer and resize our
            applet
try { offScrImage = createImage(maxWidth,maxHeight); offScrGC =
offScrImage.getGraphics(); offScrGC.setColor(Color.lightGray);
offScrGC.fillRect(0,0,maxWidth,maxHeight);
resize(maxWidth,maxHeight); }catch (Exception e)
{ e.printStackTrace(); }
//load the animation images into an array for (int i=0;i<10;i++)
            { String
imageFile = new String (\"images/Duke/T\" +String.valueOf(i+1)
            +\".gif\");
images[i] = getImage(getDocumentBase(),imageFile): //Register this
image with the tracker tracker.addImage(images[i],i); } try { //Use
tracker to make sure all the images are loaded tracker.waitForAll();
            }
catch (InterruptedException e) {} loaded = true; }
//Paint the current frame. public void paint (Graphics g) { if (loaded)
{ g.drawImage(offScrImage,0,0,this); } }
//Start ,setup our first image public void start() { if (tracker.checkID
(index)) { offScrGC.drawImage (images[index],0,0,this); } animator
            = new
Thread(this); animator.start(); }
//Run,do the animation work here. //Grab an image, pause ,grab the
            next...
public void run() { //Get the id of the current thread Thread me =
Thread.currentThread();
//If our animator thread exist,and is the current thread... while
((animatr!= null) && (animator==me)) { if ( tracker.checkID
            (index))
{ //Clear the background and get the next image
offScrGC.fillRect(0,0,100,100);
offScrGCdrawImage(images[index],0,0,this); index++; //Loop back to
            the
beginning and keep going if (index>= images.length) { index = 0;
            } }
//Delay here so animation looks normal try { animator.sleep(200);
            }catch
(InterruptedException e) {} //Draw the next frame repaint(); } } }

7.3 多 线 程 间 的 通 讯

7.3.1 生 产 者 和 消 费 者

多 线 程 的 一 个 重 要 特 点 是 它 们 ?reg; 间 可 以 互 相 通 讯。 你 可 以 设 计 线 程 使 用 公 用 对 象， 每 个 线 程 都 可 以 独 立 操 作 公 用 对 象。 典 型 的 线 程 间 通 讯 建 立 在 生 产 者 和 消 费 者 模 型 上： 一 个 线 程 产 生 输 出； 另 一 个 线 程 使 用 输 入buffer
让 我 们 创 建 一 个 简 单 的\"Alphabet Soup\"生 产 者 和 相 应 的 消 费 者.

7.3.2 生 产 者

生 产 者 将 从thread类 里 派 生： class Producer extends Thread
{ private Soup soup; private String alphabet = \"
ABCDEFGHIJKLMNOPQRSTUVWXYZ\";
public Producer(Soup s) { //Keep our own copy of the shared object soup
= s; }
public void run() { char c; //Throw 10 letters into the soup for (int
i=0;i<10;i++) { c = alphabet.charAt((int)(Math.random() *26));
soup.add(c); //print a record of osr addition
System.out.println(\"Added\"+c + \"to the soup.\"); //wait a bit before we
add the next letter try { sleep((int)(Math.random() *1000)); } catch
(InterruptedException e) {} } } }
注 意 我 们 创 建 了Soup类 的 一 个 实 例。 生 产 者 用soup.add()函 数 来 建 立 字 符 池。

7.3.3 消 费 者

让 我 们 看 看 消 费 者 的 程 序： class Consumer extends Thread { private Soup soup;
public Consumer (Soup s) { //keep our own copy of the shared object soup
= s; }
public void run() { char c; //Eat 10 letters from the alphabet soup for
(int I=0 ;i<10;i++) { //grab one letter c = soup.eat(); //Print out the
letter that we retrieved System.out.println(\"Ate a letter: \" +c); //try
{ sleep((int)(Math.raddom()*2000)); } catch (InterruptedException e) {} } } }
同 理， 象 生 产 者 一 样， 我 们 用soup.eat()来 处 理 信 息。 那 么，Soup类 到 底 干 什 么 呢？

7.3.4 监 视

Soup类 执 行 监 视 两 个 线 程 ?reg; 间 传 输 信 息 的 功 能。 监 视 是 多 线 程 中 不 可 缺 少 的 一 部 分， 因 为 它 保 持 了 通 讯 的 流 ?copy;。 让 我 们 看 看Soup.java文 件： class Soup { private char buffer[] = new char[6]; private int next = 0; //Flags to keep track of
our buffer status private boolean isFull = false; private boolean isEmpty
= true; public syschronized char eat() { //We can\'t eat if there isn\'t anything
in the buffer while (isEmpty == true) { try { wait() ;//we\'ll exit this
when isEmpty turns false }catch (InterruptedException e) {} } //decrement
the count,since we\'re going to eat one letter next--; //Did we eat the
last letter? if (next== 0) { isEmpty = true; } //We know the buffer can\'t
be full,because we just ate isFull = false; notify(); //return the letter
to the thread that is eating return (buffer[next]); }
//method to add letters to the buffer public synchronized void add(char
c) { //Wait around until there\'s room to add another letter while (isFull
== true ) { try{ wait();//This will exit when isFull turns false }catch
(InterruptedException e) {} } //add the letter to the next available spot
buffer[next]=c; //Change the next available spot next++; //Are we full;
if (next ==6) { isFull =true; } isEmpty =false; notify(); } } soup类 包 含 两 个 重 要 特 征： 数 据 成 员buffer[]是 私 有 的， 功 能 成 员add()和eat()是 公 有 的。

数 据 私 有 避 免 了 生 产 者 和 消 费 者 直 接 获 得 数 据。 直 接 访 问 数 据 可 能 造 成 错 误。 例 如， 如 果 消 费 者 企 图 从 空 缓 冲 区 里 取 出 数 据， 你 将 得 到 不 必 要 的 异 常， 否 则， 你 只 能 锁 住 进 程。 同 步 访 问 方 法 避 免 了 破 坏 一 个 共 享 对 象。 当 生 产 者 向soup里 加 入 一 个 字 母 时， 消 费 者 不 能 吃 字 符， 诸 如 此 类。 这 种 同 步 是 维 持 共 享 对 象 完 整 性 的 重 要 方 面。notify()函 数 将 唤 醒 每 一 个 等 待 线 程。 等 待 线 程 将 继 续 它 的 访 问。

7.3.5 联 系 起 来

现 在 我 们 有 一 个 生 产 者， 一 个 消 费 者 和 一 个 共 享 对 象， 怎 样 实 现 它 们 的 交 互 呢？ 我 们 只 需 要 一 个 简 单 的 控 制 程 序 来 启 动 所 有 的 线 程 并 确 信 每 一 个 线 程 都 是 访 问 的 同 一 个 共 享 对 象。 下 面 是 控 制 程 序 的 代 码，SoupTest.java:
class SoupTest { public static void main(String args[]) { Soup s = new
Soup(); Producer p1 = new Producer(s); Consumer c1 = new Consumer(s);
p1.start(); c1.start(); } }

7.3.6 监 视 生 产 者

生 产 者/消 费 者 模 型 程 序 经 常 用 来 实 现 远 程 监 视 功 能， 它 让 消 费 者 看 到 生 产 者 同 用 户 的 交 互 或 同 系 统 其 它 部 分 的 交 互。 例 如， 在 网 络 中， 一 组 生 产 者 线 程 可 以 在 很 多 工 作 站 上 运 行。 生 产 者 可 以 打 印 文 档， 文 档 打 印 后， 一 个 标 志 将 保 存 下 来。 一 个（或 多 个?copy; 消 费 者 将 保 存 标 志 并 在 晚 上 报 告 白 天 打 印 活 动 的 情 况。 另 外， 还 有 例 子 在 一 个 工 作 站 是 分 出 几 个 独 立 的 窗 口。 一 个 窗 口 用 作 用 户 输 入（生 产 者) 另 一 个 窗 口 作 出 对 输 入 的 反 应（消 费 者)。

7.4 线 程API列 表

下 面 是 一 个 常 用 的 线 程 类 的 方 法 函 数 列 表： 类 函 数： 以 下 是Thread的 静 态 函 数， 即 可 以 直 接 从Thread类 调 用。
currentThread 返 回 正 在 运 行 的Thread对 象 yield 停 止 运 行 当 前 线 程， 让 系 统 运 行 下 一 个 线 程 sleep(int n) 让 当 前 线 程 睡 眠n毫 秒 对 象 函 数： 以 下 函 数 必 须 用Thread的 实 例 对 象 来 调 用。
start start函 数 告 诉java运 行 系 统 为 本 线 程 建 立 一 个 执 行 环 境， 然 后 调 用 本 线 程 的run()函 数。 run 是 运 行 本 线 程 的 将 要 执 行 的 代 码， 也 是Runnable接 口 的 唯 一 函 数。 当 一 个 线 程 初 始 化 后， 由start函 数 来 调 用 它， 一 ?copy;run函 数 返 回， 本 线 程 也 就 终 止 了。 stop 让 某 线 程 马 上 终 止， 系 统 将 删 除 本 线 程 的 执 行 环 境 suspend 与stop函 数 不 同，suspend将 线 程 暂 停 执 行， 但 系 统 不 破 坏 线 程 的 执 行 环 境， 你 可 以 用resume来 恢 复 本 线 程 的 执 行 resume 恢 复 被 挂 起 的 线 程 进 入 运 行 状 态 setPriority(int p) 给 线 程 设 置 优 先 级 getPriority 返 回 线 程 的 优 先 级 setName(String name) 给 线 程 设 置 名 称 getName 取 线 程 的 名 称

本 章 小 结：

1.多 线 程 是java语 言 的 重 要 特 点，java语 言 用Thread类 封 装 了 线 程 的 所 有 操 作。 2.线 程 的 接 口 名 为Runnable 3.线 程 间 同 步 机 制 为synchronized关 键 词 4.线 程 间 通 讯 靠wait与notify消 息


第 八 章 Java的\" 异 常\"

\" 异 常\" 指 的 是 程 序 运 行 时 出 现 的 非 正 常 情 况。 在 用 传 统 的 语 言 编 程 时， 程 序 员 只 能 通 过 函 数 的 返 回 值 来 发 出 错 误 信 息。 这 易 于 导 致 很 多 错 误， 因 为 在 很 多 情 况 下 需 要 知 道 错 误 产 生 的 内 部 细 节。 通 常， 用 全 局 变 量errno来 存 储\" 异 常\" 的 类 型。 这 容 易 导 致 误 用， 因 为 一 个errno的 值 有 可 能 在 被 处 理 ?reg; 前 被 另 外 的 错 误 覆 盖 掉。 即 使 最 优 美 的C语 言 程 序， 为 了 处 理\" 异 常\" 情 况， 也 常 求 助 于goto语 句。 Java对\" 异 常\" 的 处 理 是 面 向 对 象 的。 一 个Java的Exception是 一 个 描 述\" 异 常\" 情 况 的 对 象。 当 出 现\" 异 常\" 情 况 时， 一 个Exception对 象 就 产 生 了， 并 放 到 产 生 这 个\" 异 常\" 的 成 员 函 数 里。

8.1 基础

Java的\" 异 常\" 处 理 是 通 过5个 关 键 词 来 实 现 的：try, catch, throw, throws和finally。 用try 来 执 行 一 段 程 序， 如 果 出 现\" 异 常\"， 系 统 抛 出（throws?copy; 一 个\" 异 常\"， 你 可 以 通 过 它 的 类 型 来 捕 捉（catch?copy; 它， 或 最 后（finally?copy; 由 缺 省 处 理 器 来 处 理。 下 面 是\" 异 常\" 处 理 程 序 的 基 本 形 式： try { //程 序 块 } catch (ExceptionType1 e) { // 对ExceptionType1的 处 理 } catch (ExceptionType2 e) { // 对ExceptionType2的 处 理 throw(e); //再 抛 出 这 个\" 异 常\" } finally { }

8.2 \"异 常\" 的 类 型

在\" 异 常\" 类 层 次 的 最 上 层 有 一 个 单 独 的 类 叫 做Throwable。 这 个 类 用 来 表 示 所 有 的\" 异 常\" 情 况。 每 个\" 异 常\" 类 型 都 是Throwable的 子 类。Throwable有 两 个 直 接 的 子 类。 一 类 是Exception， 是 用 户 程 序 能 够 捕 捉 到 的\" 异 常\" 情 况。 我 们 将 通 过 产 生 它 的 子 类 来 创 建 自 己 的\" 异 常\"。 另 一 类 是Error， 它 定 义 了 那 ?copy; 通 常 无 法 捕 捉 到 的\" 异 常\"。 要 谨 慎 使 用Error子 类， 因 为 它 们 通 常 会 导 致 灾 难 性 的 失 败。 在Exception中 有 一 个 子 类RuntimeException， 它 是 程 序 运 行 时 自 动 地 对 某 ?copy; 错 误 作 出 反 应 而 产 生 的。

8.3 不 捕 捉\" 异 常\"

\" 异 常\" 对 象 是Java在 运 行 时 对 某 ?copy;\" 异 常\" 情 况 作 出 反 应 而 产 生 的。 例 如， 下 面 这 个 小 程 序 包 含 一 个 整 数 被0除 的\" 异 常\"。
class Exc0 { public static void main(String args[]) { int d = 0; int a = 42/d; } }
当Java执 行 这 个 除 法 时， 由 于 分 母 是0， 就 会 构 造 一 个\" 异 常\" 对 象 来 使 程 序 停 下 来 并 处 理 这 个 错 误 情 况， 在 运 行 时\" 抛 出\"（throw?copy; 这 个\" 异 常\"。 说\" 抛 出\" 是 因 为 它 象 一 个 滚 烫 的 马 铃 薯， 你 必 须 把 它 抓 住 并 立 即 处 理。 程 序 流 将 会 在 除 号 操 作 符 处 被 打 断， 然 后 检 查 当 前 的 调 用 堆 栈 来 查 找\" 异 常\"。 一 个\" 异 常\" 处 理 器 是 用 来 立 即 处 理\" 异 常\" 情 况 的。 在 这 个 例 子 里， 我 们 没 有 编 一 个\" 异 常\" 处 理 器， 所 以 缺 省 的 处 理 器 就 发 挥 作 用 了。 缺 省 的 处 理 器 打 印Exception的 字 符 ?reg; 值 和 发 生 \" 异 常\" 的 地 点。
下 面 是 我 们 的 小 例 子 的 输 出。
C:\\>java Exc0 java.lang.arithmeticException: / by zero at Exc0.main(Exc0.java:4)

8.4 try与catch

通 常 我 们 希 望 自 己 来 处 理\" 异 常\" 并 继 续 运 行。 可 以 用try来 指 定 一 块 预 防 所 有\" 异 常\" 的 的 程 序。 紧 跟 在try程 序 后 面， 应 包 含 一 个catch子 句 来 指 定 你 想 要 捕 捉 的\" 异 常\" 的 类 型。 例 如， 下 面 的 例 子 是 在 前 面 的 例 子 的 基础上 构 造 的， 但 它 包 含 一 个try程 序 块 和 一 个catch子 句。 class exc1 { public static void main(string args[]) { try { int d = 0; int a = 42 / d; } catch (arithmeticexception e) { system.out.println(\"division by zero\"); } } }

catch子 句 的 目 标 是 解 决\" 异 常\" 情 况， 把 变 量 设 到 合 理 的 状 态， 并 象 没 有 出 错 一 样 继 续 运 行。 如 果 一 个 子 程 序 不 处 理 某 个\" 异 常\"， 则 返 到 上 一 级 处 理， 直 到 最 外 一 级。

8.5 多 个catch子 句

在 某 情 况 下， 同 一 段 程 序 可 能 产 生 不 止 一 种\" 异 常\" 情 况。 你 可 以 放 置 多 个catch子 句， 其 中 每 一 种\" 异 常\" 类 型 都 将 被 检 查， 第 一 个 与 ?reg; 匹 配 的 就 会 被 执 行。 如 果 一 个 类 和 其 子 类 都 有 的 话， 应 把 子 类 放 在 前 面， 否 则 将 永 远 不 会 到 达 子 类。 下 面 是 一 个 有 两 个catch子 句 的 程 序 的 例 子。
class MultiCatch { public static void main(String args[]) { try { int a
= args.length; System.out.println(\"a = \" + a); int b = 42/a; int c[] =
{1}; c[42] = 99; } catch(ArithmeticException e) { System.out.println(\"div
by 0: \" + e); } catch(ArrayIndexOutOfBoundsException e)
{ system.out.println(\"array index oob: \" + e); } } }

如 果 在 程 序 运 行 时 不 跟 参 数， 将 会 引 起 一 个0做 除 数 的\" 异 常\"， 因 为a的 值 为0。 如 果 我 们 提 ?copy; 一 个 命 令 行 参 数， 将 不 会 产 生 这 个\" 异 常\"， 因 为a的 值 大 于0。 但 会 引 起 一 个 ArrayIndexOutOfBoundexception的\" 异 常\"， 因 为 整 型 数 组c的 长 度 是1， 却 给c[42]赋 值。 下 面 是 以 上 两 种 情 况 的 运 行 结 果。
C:\\>java MultiCatch a = 0 div by 0: java.lang.arithmeticexception: / by
zero C:\\>java MutiCatch 1 a = 1 array index oob:
java.lang.ArrayIndexOutOfBoundsException:42

8.6 try语 句 的 嵌 套

你 可 以 在 一 个 成 员 函 数 调 用 的 外 面 写 一 个try语 句， 在 这 个 成 员 函 数 内 部， 写 另 一 个try语 句 保 护 其 他 代 码。 每 当 遇 到 一 个try语 句，\" 异 常\" 的 框 架 就 放 到 堆 栈 上 面， 直 到 所 有 的try语 句 都 完 成。 如 果 下 一 级 的try语 句 没 有 对 某 种\" 异 常\" 进 行 处 理， 堆 栈 就 会 展 开， 直 到 遇 到 有 处 理 这 种\" 异 常\" 的try语 句。 下 面 是 一 个try语 句 嵌 套 的 例 子。

class MultiNest { static void procedure() { try { int c[] = { 1 }: c[42]
= 99; } catch(ArrayIndexOutOfBoundsexception e)
{ System.out.println(\"array index oob: \" + e); } } public static void
main(String args[]) { try { int a = args.length; system.out.println(\"a
= \" + a); int b = 42/a; procedure(); } catch(arithmeticException e)
{ System.out.println(\"div by 0: \" + e); } } }
成 员 函 数procedure里 有 自 己 的try/catch控 制， 所 以main不 用 去 处 理 ArrayIndexOutOfBoundsException。

8.7 throw语 句

throw语 句 用 来 明 确 地 抛 出 一 个\" 异 常\"。 首 先， 你 必 须 得 到 一 个Throwable的 实 例 的 控 制 柄， 通 过 参 数 传 到catch子 句， 或 者 用new操 作 符 来 创 建 一 个。 下 面 是throw语 句 的 通 常 形 式。
throw ThrowableInstance;
程 序 会 在throw语 句 后 立 即 终 止， 它 后 面 的 语 句 执 行 不 到， 然 后 在 包 含 它 的 所 有try块 中 从 里 向 外 寻 找 含 有 与 其 匹 配 的catch子 句 的try块。 下 面 是 一 个 含 有throw语 句 的 例 子。
class ThrowDemo { static void demoproc() { try { throw new NullPointerException(\"de3mo\"); } catch(NullPointerException e) { System.out.println(\"caught inside demoproc\"); throw e; } } public static void main(String args[]) { try { demoproc(); }
catch(NullPointerException e) { system.out.println(\"recaught: \" + e); } } }

8.8 throws语 句

throws用 来 标 明 一 个 成 员 函 数 可 能 抛 出 的 各 种\" 异 常\"。 对 大 多 数Exception子 类 来 说，Java 编 译 器 会 强 迫 你 声 明 在 一 个 成 员 函 数 中 抛 出 的\" 异 常\" 的 类 型。 如 果\" 异 常\" 的 类 型 是Error或 RuntimeException， 或 它 们 的 子 类， 这 个 规 则 不 起 作 用， 因 为 这 ?copy; 在 程 序 的 正 常 部 分 中 是 不 期 待 出 现 的。 如 果 你 想 明 确 地 抛 出 一 个RuntimeException， 你 必 须 用throws语 句 来 声 明 它 的 类 型。 这 就 重 新 定 义 了 成 员 函 数
的 定 义 语 法： type method-name(arg-list) throws exception-list { }
下 面 是 一 段 程 序， 它 抛 出 了 一 个\" 异 常\"， 但 既 没 有 捕 捉 它， 也 没 有 用throws来 声 明。 这 在 编 译 时 将 不 会 通 过。
class ThrowsDemo1 { static void procedure( ) [ System.out.println(\"inside
procedure\"); throw new IllegalAccessException(\"demo\"); } public static
void main(String args[]) { procedure( ); } }
为 了 让 这 个 例 子 编 译 过 去， 我 们 需 要 声 明 成 员 函 数procedure抛 出 了IllegalAccessException， 并 且 在 调 用 它 的 成 员 函 数main里 捕 捉 它。 下 面 是 正 确 的 例 子：
class ThrowsDemo { static void procedure( ) throws IllegalAccessException
{ System.out.println(\"inside procedure\"); throw new
IllegalAccessException(\"demo\"); } public static void main(String args[])
{ try { procedure( ); } catch (IllegalAccessException e)
{ System.out.println(\"caught \" + e); } } }
下 面 是 输 出 结 果：
C:\\>java ThrowsDemo inside procedure caught
java.lang.IllegalAccessException: demo

8.9 finally

当 一 个\" 异 常\" 被 抛 出 时， 程 序 的 执 行 就 不 再 是 线 性 的， 跳 过 某 ?copy; 行， 甚 至 会 由 于 没 有 与 ?reg; 匹 配 的catch子 句 而 过 早 地 返 回。 有 时 确 保 一 段 代 码 不 管 发 生 什 么\" 异 常\" 都 被 执 行 到 是 必 要 的， 关 键 词finally就 是 用 来 标 识 这 样 一 段 代 码 的。 即 使 你 没 有catch子 句，finally程 序 块 也 会 在 执 行 try程 序 块 后 的 程 序 ?reg; 前 执 行。 每 个try语 句 都 需 要 至 少 一 个 与 ?reg; 相 配 的catch子 句 或finally子 句。 一 个 成 员 函 数 返 回 到 调 用 它 的 成 员 函 数， 或 者 通 过 一 个 没 捕 捉 到 的\" 异 常\"， 或 者 通 过 一 个 明 确 的return语 句，finally子 句 总 是 恰 好 在 成 员 函 数 返 回 前 执 行。 下 面 是 一 个 例 子， 它 有 几 个 成 员 函 数， 每 个 成 员 函 数 用 不 同 的 途 径 退 出， 但 执 行 了finally子 句。
class FinallyDemo { static void procA( ) { try
{ System.out.println(\"inside procA\"); throw new
RuntimeException(\"demo\"); } finally { System.out.println(\"procA\'s
finally\"); } } static void procB( ) { try { System.out.println(\"inside
procB\"); return; } finally { System.out.println(\"procB\'s finally\"); } }
public static void main(String args[]) { try { procA( ); } catch (Exception
e); procB( ); } }
下 面 是 这 个 例 子 的 运 行 结 果：
C:\\>java FinallyDemo inside procA procA\'s finally inside procB procB\'s finally

本 章 小 结

1. \" 异 常\" 指 的 是 程 序 运 行 时 出 现 的 非 正 常 情 况。 2. 在\" 异 常\" 类 层 次 的 最 上 层 的 类 叫Throwable， 它 有 两 个 直 接 的 子 类：Exception和Error。 3. Java的\" 异 常\" 处 理 通 过5个 关 键 词 来 实 现：try，catch，throw，throws和finally。


第 九 章 Java输 入 输 出 操 作

9.1 Java 输 入 输 出 流

所 有 的 程 序 语 言 都 提 ?copy; 与 本 机 文 件 系 统 交 互 的 方 式；Java也 不 例 外。 我 们 将 看 看Java是 怎 样 处 理 标 准 文 件 输 入 输 出 的(包 括stdin,stout,stderr)。 当 你 在 网 络 上 开 发 小 程 序 时， 你 必 须 注 意 直 接 文 件 输 入 输 出 是 不 安 全 因 素 的 关 键。 大 多 数 用 户 设 置 他 们 的 浏 览 器， 可 让 你 自 由 的 访 问 他 们 的 文 件 系 统， 但 有 ?copy; 不 让 你 访 问。 当 然， 如 果 你 开 发 你 内 部 的 应 用 程 序， 你 也 许 需 要 直 接 访 问 文 件。 标 准 输 入 输 出 Unix的 用 户， 或 其 他 基 于 命 令 行 系 统 的 用 户(如DOS)， 都 知 道 标 准 输 入 输 出 的 含 义。 标 准 输 入 文 件 是 键 盘， 标 准 输 出 文 件 是 你 的 终 端 屏 幕。 标 准 错 误 输 出 文 件 也 指 向 屏 幕， 如 果 有 必 要， 它 也 可 以 指 向 另 一 个 文 件 以 便 和 正 常 输 出 区 分。

系 统 类 Java通 过 系 统 类 达 到 访 问 标 准 输 入 输 出 的 功 能。 上 面 提 到 的 三 个 文 件 在 这 个 系 统 类 中 实 现： Stdin System.in作 为InputStream类 的 一 个 实 例 来 实 现stdin， 你 可 以 使 用read()和skip(long n)两 个 成 员 函 数。read()让 你 从 输 入 中 读 一 个 字 节，skip(long n)让 你 在 输 入 中 跳 过n个 字 节。
Stout System.out作 为PrintStream来 实 现stdout， 你 可 以 使 用print()和println()两 个 成 员 函 数。 这 两 个 函 数 支 持Java的 任 意 基 本 类 型 作 为 参 数。
Stderr System.err同stdout一 样 实 现stderr。 象System.out一 样， 你 可 以 访 问PrintStream 成 员 函 数。

9.2 标 准 输 入 输 出 例 子

这 里 有 一 个 例 子， 功 能 象Unix里 的cat或type： import java.io.* class myCat{
            public void main(String args[]) throws IOException{ int b; int count
            = 0; while ((b = System.in.read()) !=
-1){ count++; System.out.print((char)b); } System.out.println();
//blank line System.err.println(\"counted\"+count+\"total
            bytes.\"); } }

9.3 普 通 输 入 输 出 类

除 了 基 本 的 键 盘 输 入 和 屏 幕 输 出 外， 我 们 还 需 要 联 系 文 件 的 输 入 输 出。 我 们 将 学 习 下 面 几 个 类： l FileInputStream l DataInputStream l FileOutputStream l DataOutputStream

作 为 参 考， 再 列 出 一 ?copy; 特 定 应 用 的 类： l PipedInputStream l BufferedInputStream l PushBackInputStream l StreamTokenizer l PipedOutputStream l BufferedOutputStream l RandomAccessFile

我 们 不 在 此 讨 论 这 ?copy; 类， 但 你 可 以 在JAVA_HOME/src/java/io目 录 里 查 看 每 个 类 的 成 员 函 数 定 义。

9.4 文 件

在 我 们 进 行 文 件 操 作 时， 需 要 知 道 关 于 文 件 的 信 息。File类 提 ?copy; 了 一 ?copy; 成 员 函 数 来 操 纵 文 件 和 获 得 文 件 的 信 息。

9.4.1 创 建 一 个 新 的 文 件 对 象

你 可 用 下 面 三 个 方 法 来 创 建 一 个 新 文 件 对 象： File myFile; myFile = new File(\"etc/motd\"); 或 myFile = new File(\"/etc\",\"motd\"); //more useful if the directory or filename are variables 或 File myDir = new file(\"/etc\"); myFile = new File(myDir,\"motd\");
这 三 种 方 法 取 决 于 你 访 问 文 件 的 方 式。 例 如， 如 果 你 在 应 用 程 序 里 只 用 一 个 文 件， 第 一 种 创 建 文 件 的 结 构 是 最 容 易 的。 但 如 果 你 在 同 一 目 录 里 打 开 数 个 文 件， 则 第 二 种 或 第 三 种 结 构 更 好。

9.4.2 文 件 测 试 和 使 用

一 但 你 创 建 了 一 个 文 件 对 象， 你 便 可 以 使 用 以 下 成 员 函 数 来 获 得 文 件 相 关 信 息： 文 件 名 l String getName() l String getPath() l String getAbslutePath() l String getParent() l boolean renameTo(File newName) 文 件 测 试 l boolean exists() l boolean canWrite() l boolean canRead() l boolean isFile() l boolean isDirectory() l boolean isAbsolute() 一 般 文 件 信 息 l long lastModified() l long length() 目 录 用 法 l boolean mkdir() l String[] list()

9.4.3 文 件 信 息 获 取 例 子 程 序

这 里 是 一 个 独 立 的 显 示 文 件 的 基 本 信 息 的 程 序， 文 件 通 过 命 令 行 参 数 传 输：
import java.io.*; class fileInfo{ File fileToCheck; public static void main(String args[]) throws IOException{ if (args.length>0){ for (int i=0;i


第一章 Java语言的产生及其特点
第二章 Java程序开发与运行环境
第三章 Java程序设计基础
第四章 Java应用程序的基本框架
第五章 Java的类
本书由E类出版物免费制作
第六章 Java图形用户接口
第七章 多线程
第八章 Java的"异常"
第九章 Java输入输出操作