目 录 编 者 的 话 专 题 : 进 击 的 Java 1 Java 太 笨? 纯 粹 诽 谤 9 保 守 的 设 计 思 想 是 Java 的 最 大 优 势 17 如 何 在 Android 上 编 写 高 效 的 Java 代 码 47 Java 8 函 数 式 编 程 : 第 一 个 La

Similar documents
新・解きながら学ぶJava

Microsoft Word - 01.DOC

Chapter 9: Objects and Classes

建模与图形思考

Android Service

untitled

KillTest 质量更高 服务更好 学习资料 半年免费更新服务

Java

chp6.ppt

(Microsoft Word - \272\364\263q\245|\244A_ _\304\254\253\330\336\263__\272\353\302\262\263\370\247i.doc)

untitled

用手機直接傳值不透過網頁連接, 來當作搖控器控制家電 ( 電視遙控器 ) 按下按鍵發送同時會回傳值來確定是否有送出 問題 :1. 應該是使用了太多 thread 導致在傳值上有問題 2. 一次按很多次按鈕沒辦法即時反應

Java 1 Java String Date

基于CDIO一体化理念的课程教学大纲设计

Microsoft Word - 第3章.doc

KillTest 质量更高 服务更好 学习资料 半年免费更新服务

(TestFailure) JUnit Framework AssertionFailedError JUnit Composite TestSuite Test TestSuite run() run() JUnit

EJB-Programming-4-cn.doc

01_Service

建立Android新專案

9, : Java 19., [4 ]. 3 Apla2Java Apla PAR,Apla2Java Apla Java.,Apla,,, 1. 1 Apla Apla A[J ] Get elem (set A) A J A B Intersection(set A,set B) A B A B

EJB-Programming-3.PDF

untitled

untitled

前言 C# C# C# C C# C# C# C# C# microservices C# More Effective C# More Effective C# C# C# C# Effective C# 50 C# C# 7 Effective vii

2 Java 语 言 程 序 设 计 教 程 简 单 性 Java 语 言 的 语 法 与 C 语 言 和 C++ 语 言 很 接 近, 使 得 大 多 数 程 序 员 很 容 易 学 习 和 使 用 Java 另 一 方 面,Java 丢 弃 了 C++ 中 很 少 使 用 的 很 难

詞 彙 表 編 號 詞 彙 描 述 1 預 約 人 資 料 中 文 姓 名 英 文 姓 名 身 份 證 字 號 預 約 人 電 話 性 別 2 付 款 資 料 信 用 卡 別 信 用 卡 號 信 用 卡 有 效 日 期 3 住 房 條 件 入 住 日 期 退 房 日 期 人 數 房 間 數 量 入

《大话设计模式》第一章

1.JasperReport ireport JasperReport ireport JDK JDK JDK JDK ant ant...6

Java的详细介绍

<4D F736F F D20BBF9D3DA416E64726F6964C6BDCCA8B5C4B5E7D7D3C5C4C2F4CFB5CDB32E646F63>

CC213

计 算 机 系 统 应 用 年 第 25 卷 第 4 期 线 程 的 复 用 [2,3]. 通 常 情 况 下, 服 务 器 端 程 序 在 启 动 时 创 建 若 干 数 量 的 线 程 对 象 并 缓 存 起 来, 此 时 它 们 处 于

FY.DOC

2009年3月全国计算机等级考试二级Java语言程序设计笔试试题

1: public class MyOutputStream implements AutoCloseable { 3: public void close() throws IOException { 4: throw new IOException(); 5: } 6:

Android Robert C.C. Huang Oscar F.Y. Liu Peter C.L. Hsieh 2011/03/21

雲端 Cloud Computing 技術指南 運算 應用 平台與架構 10/04/15 11:55:46 INFO 10/04/15 11:55:53 INFO 10/04/15 11:55:56 INFO 10/04/15 11:56:05 INFO 10/04/15 11:56:07 INFO

untitled

概述

“百企入校——广西青年企业家协会高校

主程式 : public class Main3Activity extends AppCompatActivity { ListView listview; // 先整理資料來源,listitem.xml 需要傳入三種資料 : 圖片 狗狗名字 狗狗生日 // 狗狗圖片 int[] pic =new

全国计算机技术与软件专业技术资格(水平)考试



从 因 人 设 事 谈 起 一 部 文 学 作 品 ( 尤 其 是 长 篇 小 说 ) 的 结 构 至 关 重 要, 因 为 它 是 文 本 整 体 的 组 织 方 式 和 内 部 构 造, 既 是 形 式 又 是 内 容 ; 乃 是 表 达 主 题 最 有 效 的 艺 术 手 段 元 代 戏 曲

循经指压疗法

Microsoft Word - HERBRECIPES《中國藥膳》.doc

毛主席的猪

(京)新登字063号

背 景 概 述 企 业 需 要 一 种 灵 活 的 平 台 来 快 速 构 建 测 试 和 扩 展 新 的 应 用 程 序 服 务 并 对 市 场 中 发 生 的 数 字 化 变 革 作 出 反 应 数 字 化 变 革 正 在 加 快 步 伐, 因 为 流 程 和 信 息 的 日 益 融 合 带 来

附件1.FIT)

北魏山东佛教文化个案研究


Chapter 9: Objects and Classes

没 有 多 余 的 Contruol 或 Action 了 原 来 Domain 层 被 服 务 层 Service layer 遮 挡, 在 右 边 图 中, 则 Domain 层 直 接 暴 露 给 前 台 了, 没 有 被 遮 挡, 裸 露 了 这 样 一 步 到 位 实 现 领 域 模 型

Java java.lang.math Java Java.util.Random : ArithmeticException int zero = 0; try { int i= 72 / zero ; }catch (ArithmeticException e ) { // } 0,

使 用 Java 语 言 模 拟 保 险 箱 容 量 门 板 厚 度 箱 体 厚 度 属 性 锁 具 类 型 开 保 险 箱 关 保 险 箱 动 作 存 取 款

要 站 立 得 稳, 我 在 十 字 架 上 已 经 都 抢 夺 过 来 了, 将 魔 鬼 不 让 你 们 来 享 用 的 都 推 开 了, 这 是 让 我 们 来 得 到 的 话 语 我 们 再 也 不 被 奴 仆 的 轭 辖 制, 要 来 拥 有 才 可 以 明 知 道 却 不 去 抢 夺 过

Learning Java

國家圖書館典藏電子全文

untitled

1 Framework.NET Framework Microsoft Windows.NET Framework.NET Framework NOTE.NET NET Framework.NET Framework 2.0 ( 3 ).NET Framework 2.0.NET F

D C 93 2

KillTest 质量更高 服务更好 学习资料 半年免费更新服务

untitled

Microsoft Word - ch04三校.doc

Microsoft Word - PHP7Ch01.docx

JavaIO.PDF

新版 明解C++入門編

vi JSON JSON API XML JSON JSON JavaScript RESTful JSON Douglas Crockford JSON / RESTful API JavaScript Node.js Ruby on Rails Java Groovy

在 ongodb 中实现强事务

中 文 摘 要 智 慧 型 手 機 由 於 有 強 大 的 功 能, 以 及 優 渥 的 便 利 性, 還 能 與 網 路 保 持 隨 時 的 鏈 結 與 同 步 更 新, 因 此 深 受 廣 大 消 費 者 喜 愛, 當 然, 手 機 遊 戲 也 成 為 現 代 人 不 可 或 缺 的 娛 樂 之


IoC容器和Dependency Injection模式.doc

Microsoft PowerPoint - plan08.ppt

多層次傳銷與獎金系統

untitled

BOOL EnumWindows(WNDENUMPROC lparam); lpenumfunc, LPARAM (Native Interface) PowerBuilder PowerBuilder PBNI 2

<4D F736F F F696E74202D20332D322E432B2BC3E6CFF2B6D4CFF3B3CCD0F2C9E8BCC6A1AAD6D8D4D8A1A2BCCCB3D0A1A2B6E0CCACBACDBEDBBACF2E707074>

二 筒 答 题 { 共 4 题, 每 题 1 0 分, 共 4 0 分 } 5. 简 述 教 育 与 社 会 实 践 相 结 合 的 现 实 意 义 6. 简 述 科 教 兴 国 战 略 的 涵 义 7. 简 述 科 学 精 神 的 涵 义 8. 简 述 创 新 教 育 思 想 对 学 校 教 育

Microsoft Word - 第1章 Android基本概念.docx

javaexample-02.pdf

WebSphere Studio Application Developer IBM Portal Toolkit... 2/21 1. WebSphere Portal Portal WebSphere Application Server stopserver.bat -configfile..

KillTest 质量更高 服务更好 学习资料 半年免费更新服务

Microsoft Word - 11.doc


3.1 num = 3 ch = 'C' 2

untitled

Java ¿ª·¢ 2.0: Óà Hadoop MapReduce ½øÐдóÊý¾Ý·ÖÎö

基于ECO的UML模型驱动的数据库应用开发1.doc

untitled

天津天狮学院关于修订2014级本科培养方案的指导意见

09 (File Processes) (mkdir) 9-3 (createnewfile) 9-4 (write) 9-5 (read) 9-6 (deletefile) 9-7 (deletedir) (Exercises)

利用Java技术编写桌面软件基础

(\244j\257d\276\307\274\351_ C.indd_70%.pdf)

目次 

Swing-02.pdf

出言成章中公教育专家历年研究发现

Java Access 5-1 Server Client Client Server Server Client 5-2 DataInputStream Class java.io.datainptstream (extends) FilterInputStream InputStream Obj

PowerPoint Presentation

Transcription:

16 Java 太 笨? 如 何 在 Android 上 编 写 高 效 的 代 码 第 一 个 Lambda 表 达 式 多 线 程 面 试 问 题 汇 总 从 Java 到 Groovy 惠 新 宸 : 我 也 曾 经 是 不 适 合 编 程 的 人 郭 霖 : 从 Java 程 序 员 到 Android 开 发 者

目 录 编 者 的 话 专 题 : 进 击 的 Java 1 Java 太 笨? 纯 粹 诽 谤 9 保 守 的 设 计 思 想 是 Java 的 最 大 优 势 17 如 何 在 Android 上 编 写 高 效 的 Java 代 码 47 Java 8 函 数 式 编 程 : 第 一 个 Lambda 表 达 式 58 Java 多 线 程 面 试 问 题 汇 总 94 从 Java 到 Groovy 人 物 107 惠 新 宸 : 我 也 曾 经 是 不 适 合 编 程 的 人 114 郭 霖 : 从 Java 程 序 员 到 Android 开 发 者 的 第 一 步 鲜 阅 119 自 学 是 否 可 能 动 手 125 Git 常 用 命 令 笔 记 132 异 步 编 程 使 用 初 探 码 农 1

践 行 159 每 天 叫 醒 我 的 不 是 闹 钟, 而 是 梦 想 九 卦 162 极 客 爱 情 : 程 序 员 遇 上 程 序 员 书 榜 169 看 看 大 家 都 在 读 什 么? 172 电 子 书 榜 妙 评 173 代 码 之 髓 非 常 好 的 程 序 语 言 通 识 读 本 175 活 过 是 一 种 荣 幸 读 每 个 人 都 会 死, 但 我 总 以 为 自 己 不 会 码 农 2

编 者 的 话 进 击 的 Java IEEE Spectrum 评 出 的 2014 年 十 大 语 言,Java 依 然 稳 坐 第 一 虽 然 每 个 程 序 员 几 乎 都 对 Java 有 着 无 尽 的 怨 言, 但 是 事 实 证 明, 作 为 一 个 工 具 Java 仍 然 是 最 好 用 的 编 者 / 李 盼 Java 的 设 计 思 想 保 守, 它 不 愿 意 为 增 加 新 特 性 而 增 加 新 问 题, 也 不 愿 意 让 某 些 功 能 吓 跑 一 线 的 蓝 领 工 作 者 虽 然 缓 慢, 但 是 Java 却 在 不 断 地 前 进 和 变 化 着 变 化 解 决 痛 点,Java 8 引 入 了 Lambda 表 达 式, 这 种 紧 凑 的 传 递 行 为 的 方 式 会 让 程 序 员 的 工 作 更 简 单 但 是 为 了 让 编 程 更 有 趣, 你 也 不 妨 学 习 一 种 能 在 JVM 上 运 行 的 动 态 语 言, 作 为 Java 程 序 员, 也 没 有 必 要 切 换 到 一 门 不 同 的 语 言 保 留 了 Java 语 义 的 Groovy 就 像 你 已 经 熟 知 的 语 言 外 加 一 些 扩 展, 但 动 态 语 言 会 使 简 单 的 事 情 更 简 单, 复 杂 的 事 情 也 可 以 掌 控 Android 开 发 已 经 成 为 Java 语 言 的 一 大 生 命 线 对 于 熟 悉 Java 的 开 发 者 来 说, 学 习 Android 开 发 确 实 相 对 容 易, 但 是 两 者 之 间 也 有 不 少 差 别 比 如, 曾 经 无 限 量 的 RAM 和 CPU 不 复 存 在, 而 Android 开 发 则 需 要 密 切 关 注 性 能 和 内 存 分 配 第 一 行 代 码 的 作 者 郭 霖 从 一 位 Java 程 序 员 变 成 了 一 位 Android 开 发 者, 他 认 为 写 代 码 只 是 第 一 步, 读 代 码 更 加 重 要 Everything is Byte, 只 要 拥 有 扎 实 的 基 础, 相 信 你 未 来 所 走 的 路 一 定 会 越 来 越 宽 广 码 农 1

本 期 另 外 一 位 码 农 惠 新 宸, 是 国 内 最 有 影 响 力 的 PHP 技 术 专 家 从 一 个 在 网 上 搜 PHP 教 程 的 初 学 者, 到 国 内 唯 一 的 PHP 开 发 组 核 心 成 员, 他 觉 得 整 个 过 程 就 像 打 怪 升 级 和 Java 一 样 也 被 称 为 蓝 领 语 言 的 PHP, 在 他 看 来 恰 恰 是 能 够 培 养 更 多 编 程 者 的 摇 篮, 而 人 人 都 能 编 程 的 时 代, 势 必 会 产 生 更 多 能 够 推 动 技 术 进 步 的 牛 人 我 们 对 生 产 力 的 需 求 越 来 越 高, 但 是 代 码 仍 然 是 写 出 来 的 也 许 有 些 事 永 远 都 无 法 在 一 个 语 言 中 改 变, 但 是 相 对 于 语 法, 也 许 我 们 更 应 该 关 心 的 是 能 用 语 言 来 实 现 什 么 码 农 2

专 题 : 进 击 的 Java Java 太 笨? 纯 粹 诽 谤 作 者 /Ben Evans Ben Evans 是 jclarity 的 联 合 创 始 人 其 公 司 致 力 于 开 发 可 以 为 开 发 和 运 维 团 队 提 供 帮 助 的 性 能 工 具 和 服 务 他 是 LJC( 伦 敦 Java 用 户 组 ) 的 组 织 者 之 一, 也 是 JCP (Java 社 区 过 程 ) 执 行 委 员 会 的 成 员 之 一, 帮 助 定 义 Java 生 态 系 统 中 的 一 些 标 准 他 还 是 Java Champion 荣 誉 得 主 他 曾 与 人 合 著 了 Java 程 序 员 修 炼 之 道 (The Well-Grounded Java 如 果 你 用 Java 做 过 大 项 目, 可 能 已 经 注 意 到 了,Java 有 时 稍 显 繁 琐 和 笨 拙 你 甚 至 可 能 希 望 它 不 是 这 样 的 总 之 要 再 容 易 点 儿 下 面 我 们 将 介 绍 一 个 扩 展 示 例, 它 突 出 了 Java 语 言 中 一 些 恼 人 的 地 方, 指 出 了 它 未 来 的 发 展 方 向 为 函 数 式 编 程 风 格 假 设 你 要 在 一 个 交 易 ( 事 务 ) 处 理 系 统 中 编 写 一 个 新 组 件 这 个 系 统 的 简 化 视 图 如 图 1 所 示 图 1 交 易 处 理 系 统 的 例 子 码 农 1

Developer) 和 Java 权 威 技 术 手 册 ( 第 6 版 ) (Java in a Nutshell) 他 曾 就 Java 平 台 性 能 并 发 和 相 关 主 题 发 表 过 多 次 演 讲 在 图 中 可 以 看 到, 系 统 有 两 个 数 据 源 : 上 游 的 收 单 系 统 ( 可 以 通 过 Web 服 务 查 询 ) 和 下 游 的 派 发 数 据 库 这 是 一 个 很 现 实 的 系 统, 是 Java 开 发 人 员 经 常 构 建 的 系 统 我 们 在 这 里 准 备 引 入 一 小 段 代 码 把 两 个 数 据 源 整 合 起 来 你 会 看 到 Java 解 决 这 个 问 题 有 点 笨 拙 之 后 我 们 会 介 绍 函 数 式 编 程 的 一 个 核 心 概 念, 并 展 示 一 下 怎 么 用 映 射 (map) 和 过 滤 器 (filter) 等 函 数 式 特 性 简 化 很 多 常 见 的 编 程 任 务 你 会 看 到 Java 由 于 缺 乏 对 这 些 特 性 的 直 接 支 持, 编 程 会 困 难 不 少 整 合 系 统 我 们 需 要 一 个 整 合 系 统 来 检 查 数 据 确 实 到 了 数 据 库 这 个 系 统 的 核 心 是 reconcile() 方 法, 它 有 两 个 参 数 :sourcedata( 来 自 于 Web 服 务 的 数 据, 归 结 到 一 个 Map 中 ) 和 dbids 你 需 要 从 sourcedata 中 取 出 main_ref 键 值, 用 它 跟 数 据 库 记 录 的 主 键 比 较 代 码 清 单 1 是 进 行 比 较 的 代 码 代 码 清 单 1 整 合 两 个 数 据 源 public void reconcile(list<map<string, String>> sourcedata, Set<String> dbids) { Set<String> seen = new HashSet <String>(); MAIN: for (Map<String, String> row : sourcedata) { String ptraderef = row.get("main_ref"); // 假 定 ptraderef 永 远 不 会 为 null if (dbids.contains(ptraderef)) { System.out.println(pTradeRef +" OK"); seen.add(ptraderef); else { System.out.println("main_ref: "+ ptraderef +" not present in DB"); 码 农 2

for (String tid : dbids) { // 特 殊 情 况 if (!seen.contains(tid)) { System.out.println("main_ref: "+ tid +" seen in DB but not Source"); 这 里 主 要 是 检 查 收 单 系 统 中 的 所 有 订 单 是 否 都 出 现 在 派 发 数 据 库 里 这 项 检 查 由 打 上 了 MAIN 标 签 的 for 循 环 来 做 还 有 另 外 一 种 可 能 比 如 有 个 实 习 生 通 过 管 理 界 面 做 了 些 测 试 订 单 ( 他 没 意 识 到 这 些 订 单 用 的 是 生 产 系 统 ) 这 样 订 单 数 据 会 出 现 在 派 发 数 据 库 里, 但 不 会 出 现 在 收 单 系 统 中 为 了 处 理 这 种 特 殊 情 况, 还 需 要 一 个 循 环 这 个 循 环 要 检 查 所 见 到 的 集 合 ( 同 时 出 现 在 两 个 系 统 中 的 交 易 ) 是 否 包 含 了 数 据 库 中 的 全 部 记 录 它 还 会 确 认 那 些 遗 漏 项 下 面 是 这 个 样 例 的 一 部 分 输 出 : 7172329 OK 1R6GV OK 1R6GW OK main_ref: 1R6H2 not present in DB main_ref: 1R6H3 not present in DB 1R6H6 OK 哪 儿 出 错 了? 原 来 是 上 游 系 统 不 区 分 大 小 写 而 下 游 系 统 区 分, 在 派 发 数 据 库 里 表 示 为 1R6H12 的 记 录 实 际 上 是 1r6h2 如 果 你 检 查 一 下 代 码 清 单 7-1, 就 会 发 现 问 题 出 在 contains() 方 法 上 码 农 3

contains() 方 法 会 检 查 其 参 数 是 否 出 现 在 目 标 集 合 中, 只 有 完 全 匹 配 时 才 会 返 回 true 也 就 是 说 其 实 你 应 该 用 containscaseinsensitive() 方 法, 可 这 是 一 个 根 本 就 不 存 在 的 方 法! 所 以 你 必 须 把 下 面 这 段 代 码 if (dbids.contains(ptraderef)) { System.out.println(pTradeRef +" OK"); seen.add(ptraderef); else { System.out.println("main_ref: "+ ptraderef +" not present in DB"); 换 成 这 样 的 循 环 : for (String id : dbids) { if (id.equalsignorecase(ptraderef)) { System.out.println(pTradeRef +" OK"); seen.add(ptraderef); continue MAIN; System.out.println("main_ref: "+ ptraderef +" not present in DB"); 这 看 起 来 比 较 笨 重 只 能 在 集 合 上 执 行 循 环 操 作, 不 能 把 它 当 成 一 个 整 体 来 处 理 代 码 既 不 简 洁, 又 似 乎 很 脆 弱 随 着 应 用 程 序 逐 渐 变 大, 简 洁 会 变 得 越 来 越 重 要 为 了 节 约 脑 力, 你 需 要 简 洁 的 代 码 码 农 4

函 数 式 编 程 的 基 本 原 理 希 望 上 面 的 例 子 中 的 两 个 观 点 引 起 了 你 的 注 意 将 集 合 作 为 一 个 整 体 处 理 要 比 循 环 遍 历 集 合 中 的 内 容 更 简 洁, 通 常 也 会 更 好 如 果 能 在 对 象 的 现 有 方 法 上 加 一 点 点 逻 辑 来 调 整 它 的 行 为 是 不 是 很 棒 呢? 如 果 你 遇 到 过 那 种 基 本 就 是 你 需 要, 但 又 稍 微 差 点 儿 意 思 的 集 合 处 理 方 法, 你 就 明 白 不 得 不 再 写 一 个 方 法 是 多 么 沮 丧 了, 而 函 数 式 编 程 (FP) 恰 好 搔 到 了 这 个 痒 处 换 种 说 法, 简 洁 ( 并 且 安 全 ) 的 面 向 对 象 代 码 的 主 要 限 制 就 是, 不 能 在 现 有 方 法 上 添 加 额 外 的 逻 辑 这 将 我 们 引 向 了 FP 的 大 思 路 : 假 定 确 实 有 办 法 向 方 法 中 添 加 自 己 的 代 码 来 调 整 它 的 功 能 这 意 味 着 什 么? 要 在 已 经 固 定 的 代 码 中 添 加 自 己 的 处 理 逻 辑, 就 需 要 把 代 码 块 作 为 参 数 传 到 方 法 中 下 面 这 种 代 码 才 是 我 们 真 正 想 要 的 ( 为 了 突 出, 我 们 把 这 个 特 殊 的 contains() 方 法 加 粗 了 ): if (dbids.contains(ptraderef, matchfunction)) { System.out.println(pTradeRef +" OK"); seen.add(ptraderef); else { System.out.println("main_ref: "+ ptraderef +" not present in DB"); 如 果 能 这 样 写,contains() 方 法 就 能 做 任 何 检 查, 比 如 匹 配 区 分 大 小 写 这 需 要 能 把 匹 配 函 数 表 示 成 值, 即 能 把 一 段 代 码 写 成 函 数 字 面 值 并 赋 值 给 一 个 变 量 函 数 式 编 程 要 把 逻 辑 ( 一 般 是 方 法 ) 表 示 成 值 这 是 FP 的 核 心 思 想, 我 们 还 会 再 次 讨 论, 先 看 一 个 带 点 儿 FP 思 想 的 Java 例 子 码 农 5

映 射 与 过 滤 器 我 们 把 例 子 稍 微 展 开 一 些, 并 放 在 调 用 reconcile() 的 上 下 文 中 : reconcile(sourcedata, new HashSet<String>(extractPrimaryKeys(dbIn fos))); private List<String> extractprimarykeys(list<dbinfo> dbinfos) { List<String> out = new ArrayList<>(); for (DBInfo tinfo : dbinfos) { out.add(tinfo.primary_key); return out; extractprimarykeys() 方 法 返 回 从 数 据 库 对 象 中 取 出 的 主 键 值 ( 字 符 串 ) 列 表 FP 粉 管 这 叫 map() 表 达 式 :extractprimarykeys() 方 法 按 顺 序 处 理 List 中 的 每 个 元 素, 然 后 再 返 回 一 个 List 上 面 的 代 码 构 建 并 返 回 了 一 个 新 列 表 注 意, 返 回 的 List 中 元 素 的 类 型 (String) 可 能 跟 输 入 的 List 中 元 素 的 类 型 (DBInfo) 不 同, 并 且 原 始 列 表 不 会 受 到 任 何 影 响 这 就 是 函 数 式 编 程 名 称 的 由 来, 函 数 的 行 为 跟 数 学 函 数 一 样 函 数 f(x)=x*x 不 会 改 变 输 入 值 2, 只 会 返 回 一 个 不 同 的 值 4 便 宜 的 优 化 技 巧 调 用 reconcile() 时, 有 个 实 用 但 小 有 难 度 的 技 巧 : 把 extractprimarykeys() 返 回 的 List 传 入 HashSet 构 造 方 法 中, 变 成 Set 这 样 可 以 去 掉 List 中 的 重 复 元 素,reconcile() 方 法 调 用 的 contains() 可 以 少 做 一 些 工 作 码 农 6

map() 是 经 典 的 FP 惯 用 语 它 经 常 和 另 一 个 知 名 模 式 成 对 出 现 :filter() 形 态, 请 看 代 码 清 单 2 代 码 清 单 2 过 滤 器 形 态 今 天, 掌 握 JVM 上 的 新 语 言 对 Java 开 发 人 员 的 意 义 非 比 寻 常 因 此 Java 程 序 员 修 炼 之 道 除 了 深 入 探 讨 Java 关 键 技 术, 还 用 较 大 篇 幅 全 面 讨 论 了 JVM 上 的 多 语 言 开 发 和 项 目 控 制, 包 括 Groovy Scala 和 Clojure 这 些 优 秀 的 新 语 言 这 些 技 术 可 以 帮 助 Java 开 发 人 员 构 建 下 一 代 商 业 软 件 Java 开 发 人 员 若 要 修 炼 进 阶, 本 书 绝 对 不 容 错 过! 本 文 节 选 自 Java 程 序 员 修 炼 之 道 List<Map<String, String>> filtercancels(list<map<string, String>> in) { List<Map<String, String>> out = new ArrayList<>(); // 防 御 性 复 制 for (Map<String, String> msg : in) { if (!msg.get("status").equalsignorecase("cancelled")) { out.add(msg); return out; 注 意 其 中 的 防 御 性 复 制, 它 的 意 思 是 我 们 返 回 了 一 个 新 的 List 实 例 这 段 代 码 没 有 修 改 原 有 的 List(filter() 的 行 为 跟 数 学 函 数 一 样 ) 它 用 一 个 函 数 测 试 每 个 元 素, 根 据 函 数 返 回 的 boolean 值 构 建 新 的 List 如 果 测 试 结 果 为 true, 就 把 这 个 元 素 添 加 到 输 出 List 中 为 了 使 用 过 滤 器, 还 需 要 一 个 函 数 来 判 断 是 否 应 该 把 某 个 元 素 包 括 在 内 你 可 以 把 它 想 象 成 一 个 向 每 个 元 素 提 问 问 题 的 函 数 : 我 应 该 允 许 你 通 过 过 滤 器 吗? 这 种 函 数 叫 做 谓 词 函 数 (predicate function) 这 里 有 一 个 用 伪 代 码 ( 几 乎 就 是 Scala) 编 写 的 方 法 : (msg) -> {!msg.get("status").equalsignorecase("cancelled") 这 个 函 数 接 受 一 个 参 数 (msg) 并 返 回 boolean 值 如 果 msg 被 取 消 了, 码 农 7

它 会 返 回 false, 否 则 返 回 true 用 在 过 滤 器 中 时, 它 会 过 滤 掉 所 有 被 取 消 的 消 息 这 就 是 你 想 要 的 在 调 用 整 合 代 码 之 前, 你 需 要 移 除 所 有 被 取 消 的 订 单, 因 为 被 取 消 的 订 单 不 会 出 现 在 派 发 数 据 库 中 事 实 上,Java 8 准 备 采 用 这 种 写 法 ( 受 到 了 Scala 和 C# 语 法 的 强 烈 影 响 ) 我 们 接 着 往 下 看, 讨 论 一 下 其 他 情 况, 从 JVM 上 可 用 的 语 言 类 型 开 始 ( 有 时 候 我 们 也 把 这 称 为 语 言 生 态 学 ) 码 农 8

专 题 : 进 击 的 Java 保 守 的 设 计 思 想 是 Java 的 最 大 优 势 Ben Evans 是 jclarity 的 联 合 创 始 人 其 公 司 致 力 于 开 发 可 以 为 开 发 和 运 维 团 队 提 供 帮 助 的 性 能 工 具 和 服 务 他 是 LJC( 伦 敦 Java 用 户 组 ) 的 组 织 者 之 一, 也 是 JCP(Java 社 区 过 程 ) 执 行 委 员 会 的 成 员 之 一, 帮 助 定 义 Java 生 态 系 统 中 的 一 些 标 准 他 还 是 Java Champion 荣 誉 得 主 他 曾 与 人 合 著 了 Java 程 序 员 修 炼 之 道 (The Well-Grounded Java Developer) 和 Java 权 威 技 术 手 册 ( 第 6 版 ) (Java in a Nutshell) 他 曾 就 Java 平 台 性 能 并 发 和 相 关 主 题 发 表 过 多 次 演 讲 问 : Java 权 威 技 术 手 册 (Java in a Nutshell) 是 一 部 经 典, 它 的 上 一 版 ( 第 5 版 ) 长 达 1200 页, 而 在 十 年 后 即 将 在 中 国 出 版 的 第 6 版 却 只 有 400 页, 这 两 版 之 间 到 底 有 什 么 样 的 变 化? 对 于 Java 来 说, 这 十 年 意 味 着 什 么? 第 1 版 Java 权 威 技 术 手 册 是 在 Java 刚 刚 变 得 流 行 之 后 很 快 出 版 的, 那 个 时 候 人 们 对 于 Java 充 满 了 想 象 在 接 下 来 的 五 个 版 本 里, 这 本 书 越 变 越 大, 内 容 不 断 延 续 所 以 每 一 本 的 重 点 都 码 农 9

有 些 着 眼 于 历 史, 因 为 几 个 版 本 之 间 有 着 演 进 的 关 系 这 是 由 几 个 因 素 造 成 的 结 果, 有 一 部 分 原 因 纯 粹 是 因 为 这 样 比 较 好 写, 你 只 要 知 道 这 个 版 本 和 上 一 个 版 本 相 比 增 减 了 什 么 就 可 以 了 但 是 更 重 要 的 是, 在 早 些 时 候, 在 大 部 分 企 业 中,Java 的 生 命 周 期 很 长, 所 以 你 经 常 可 以 看 到 很 老 版 本 的 Java 所 以 理 解 不 同 版 本 之 间 的 区 别 变 得 很 重 要 所 以 当 你 在 某 家 公 司 某 个 Java 版 本 上 工 作 时, 如 果 你 知 道 版 本 是 哪 个, 你 也 就 知 道 了 能 做 什 么 不 能 做 什 么, 以 及 这 个 版 本 与 其 他 版 本 相 比 有 什 么 样 的 改 变 这 就 是 当 我 开 始 写 新 一 版 时, 第 一 个 想 改 变 的 事 情 因 为 现 在 最 新 版 的 Java 8( 说 到 这 里, 容 易 让 人 有 些 混 淆, Java 权 威 技 术 手 册 的 第 6 版 讲 的 是 Java 的 第 8 版 ) 的 生 命 周 期 比 以 前 短 了 很 多 当 然, 这 要 取 决 于 具 体 领 域, 但 是 了 解 了 普 遍 的 使 用 情 况 后, 你 会 发 现 使 用 老 版 本 的 ( 比 如 Java 6) 只 是 极 少 数 的 人 当 然 仍 然 有 一 些 疯 狂 的 人 仍 然 在 使 用 6 以 前 的 版 本, 大 部 分 的 用 户 使 用 的 是 版 本 7 现 在, 版 本 8 正 在 以 很 快 的 速 度 占 领 市 场, 仅 仅 在 半 年 时 间, 有 15% 到 20% 的 用 户 已 经 在 使 用 Java 8 了 所 以 新 版 Java 权 威 技 术 手 册 的 关 注 点 比 以 往 更 加 开 放, 我 们 开 始 关 注 Java 以 外 的 世 界, 也 会 讨 论 现 在 和 未 来 第 二 件 重 要 的 改 变 是 长 度, 正 如 你 所 说, 第 5 版 长 达 1200 页, 而 第 六 版 只 有 它 的 三 分 之 一, 当 我 开 始 着 手 整 理 上 一 版 的 时 候, 我 做 的 第 一 件 事 就 是 去 掉 了 三 分 之 二 的 内 容 如 果 你 看 过 第 五 版, 你 就 知 道 除 了 讨 论 Java 的 主 要 内 容 之 外, 这 本 书 的 第 二 部 分 是 关 于 索 引 的 当 然 这 也 是 历 史 原 因 造 成 的, 当 Java 刚 出 来 的 时 候, 网 络 的 使 用 还 不 是 很 普 遍, 所 以 书 自 带 一 份 索 引 是 很 合 理 的 事 但 是 还 有 另 外 两 个 原 因,Java 平 台 变 得 越 来 越 大, 在 Java 权 威 技 术 手 册 的 第 一 章 我 讨 论 了 Java 版 本 进 化, 指 出 了 Java 平 台 的 增 长 速 度 之 快 要 描 述 Java 5 平 台 的 基 本 知 识 就 用 了 800 页, 那 还 是 Java 5 的 时 候, 如 果 用 同 样 的 方 法 讨 论 Java 8 的 话, 这 本 书 就 要 顶 到 天 花 板 上 了 可 见 这 个 方 法 不 可 行 了, 没 有 人 会 愿 意 拿 着 这 样 一 本 书 另 外 一 个 原 因 ( 也 许 这 两 件 事 是 相 关 的 ) 就 是 人 们 利 用 技 术 码 农 10

信 息 的 方 式 发 生 了 改 变, 因 特 网 如 今 无 处 不 在, 人 们 不 愿 意 携 带 厚 重 的 书 籍, 所 有 的 索 引 都 可 以 在 网 上 的 PDF 网 站 等 资 源 中 找 到 所 以 这 个 时 候 还 要 在 纸 质 书 的 后 面 加 上 厚 重 的 索 引 就 很 不 合 理 了 这 就 是 Java 权 威 技 术 手 册 两 个 版 本 之 间 的 重 大 改 变 我 尝 试 的 第 三 件 事 是 捕 捉 Java 的 变 化 趋 势 和 规 律 所 以 很 多 关 于 过 去 如 何 使 用 Java 的 内 容 都 被 去 掉 了, 我 加 入 了 更 多 现 代 的 使 用 方 法, 读 者 们 需 要 了 解 垃 圾 回 收, 内 存 分 配, 并 发 编 程, 我 在 这 些 方 面 讲 了 很 多 这 里 面 涉 及 了 大 量 的 工 作, 因 为 原 来 的 版 本 都 着 力 于 保 存 原 有 的 内 容 在 第 五 版 留 下 的 三 分 之 一 内 容 中, 大 概 只 有 25%-30% 留 到 了 第 六 版 中, 这 些 内 容 也 都 重 新 编 辑 整 理 过, 而 剩 下 的 70% 左 右 的 内 容 则 是 全 新 的 因 为, 我 们 想 在 Java 8 投 入 使 用 的 时 候 就 把 这 本 书 弄 出 来, 所 以 Java 8 还 没 有 出 来 的 时 候 我 们 就 动 笔 了, 到 了 后 来 还 出 现 了 一 些 一 开 始 没 有 想 到 的 工 作 ( 比 如 Java 8 上 隐 藏 的 特 性 ) 但 是 这 次 我 们 的 经 验 更 丰 富 了, 所 以 我 们 希 望 能 在 2016 年 4 月,Java 9 出 来 的 时 候 完 成 下 一 个 版 本 事 实 上 我 已 经 做 好 具 体 计 划 了 问 : 对 于 Java 程 序 员 修 炼 之 道 (The Well-Grounded Java Developer) 你 有 下 一 步 的 计 划 吗? Java 程 序 员 修 炼 之 道 这 个 项 目 很 好, 写 作 的 过 程 也 很 愉 快 但 是 在 写 作 Java 权 威 技 术 手 册 的 过 程 中 我 消 耗 了 大 量 精 力, 我 认 为 我 可 能 不 会 再 写 这 本 书 的 第 2 版 了 我 和 这 本 书 的 原 出 版 商 Manning 谈 过 了, 但 是 最 新 的 进 展 我 并 不 了 解, 所 以 很 有 可 能 这 本 书 不 会 再 有 第 2 版 了 问 : 一 位 Java 大 牛 和 一 位 普 通 Java 程 序 员 之 间 的 区 别 是 什 么? 我 认 为 可 以 把 程 序 员 的 层 次 看 作 一 个 金 字 塔, 其 中 可 以 大 致 分 成 3 个 层 次 在 最 底 层 的 是 很 勤 劳 的 程 序 员, 但 是 他 们 可 能 对 编 程 本 身 兴 趣 不 大, 码 农 11

他 们 也 能 做 好 工 作, 但 是 他 们 下 班 之 后 就 不 会 再 想 关 于 编 程 的 事 这 是 很 正 常 的 现 象, 软 件 业 需 要 很 多 程 序 员, 并 且 这 个 需 求 仍 然 在 不 断 增 长 中 间 层 次 上 的 程 序 员, 想 再 多 做 一 些, 他 们 阅 读 科 技 新 闻 和 网 站 上 的 消 息, 他 们 会 跟 进 下 一 个 版 本 的 进 展, 他 们 关 心 自 己 的 技 能, 这 个 层 次 的 程 序 员 很 有 趣 而 最 上 层 的 程 序 员 则 是 时 刻 对 技 艺 以 及 技 术 的 本 质 着 迷 当 你 到 达 了 这 个 金 字 塔 的 最 顶 层 时, 你 就 会 开 始 有 反 馈 环, 你 可 以 从 自 身 学 习, 对 技 艺 的 了 解 也 更 深 刻 但 是 我 认 为 最 难 的 部 分 就 是 如 何 从 第 二 层 突 破 到 最 顶 层 如 果 你 对 你 所 做 工 作 之 外 的 知 识 有 一 丁 点 兴 趣, 你 就 要 寻 找 属 于 自 己 的 那 个 点, 这 个 点 对 于 每 个 人 都 不 一 样, 一 旦 发 现 那 个 让 你 着 迷 的 领 域, 你 就 可 以 随 着 好 奇 心 的 驱 使 深 入 学 习 下 去 关 于 开 源 软 件 有 一 个 说 法, 一 个 好 的 开 源 开 发 者 必 须 找 到 自 己 的 痛 点, 他 们 不 得 不 去 解 决 这 个 困 扰 他 们 的 问 题 这 是 大 多 数 人 对 开 源 软 件 感 兴 趣 的 原 因, 也 是 很 多 人 称 为 Java 开 发 者 的 原 因 你 找 到 了 一 个 让 你 感 兴 趣 的 点, 由 于 不 明 所 以, 你 一 直 学 习 下 去, 这 就 是 成 长 的 秘 密 问 : 虽 然 Lambda 加 入 了 Java 8, 但 是 在 开 发 者 之 间 始 终 有 关 于 Java 语 法 过 于 冗 长 的 抱 怨 你 认 为 这 是 很 多 开 发 者 和 团 队 不 愿 意 使 用 Java 的 主 要 原 因 吗? 我 不 这 么 认 为 James Gosling 有 三 句 话 可 以 解 释 Java 的 语 言 设 计, 以 及 为 什 么 Java 是 现 在 这 个 样 子 第 一 句 就 是 英 语 中 所 说 的 蓝 领 语 言, 蓝 领 工 人 是 从 事 第 一 线 工 作 的 人, 而 白 领 则 代 表 了 办 公 室 以 及 经 理 们 的 工 作 Java 就 是 一 种 蓝 领 语 言, 它 的 设 计 是 为 了 让 工 作 中 的 程 序 员 解 决 真 正 的 问 题 Java 是 实 用 的 语 言, 它 解 决 的 是 真 实 世 界 中 的 业 务 James Gosling 在 2014 年 JavaOne 大 会 上 谈 到 了 Lambda 以 及 Java 的 早 期 版 本 中 没 有 出 现 的 一 些 设 计, 他 说 : 如 果 我 没 有 找 到 完 成 一 件 事 的 正 确 方 法, 那 我 就 什 么 都 不 做 这 句 话 表 达 了 一 种 缓 慢 而 保 守 的 演 进 设 码 农 12

计 思 想, 要 想 理 解 Java 是 什 么, 就 必 须 要 明 白 这 点 很 多 人 觉 得 Java 老 了, 编 程 语 言 需 要 改 变, 但 是 他 们 没 有 搞 清 楚 的 是, 真 正 变 化 的 是 他 们 自 己 他 们 在 能 力 上 有 了 发 展, 他 们 想 看 得 更 远 更 深, 而 语 言 反 映 出 了 这 一 点 并 不 是 语 言 需 要 改 变, 而 是 提 出 这 个 观 点 的 程 序 员 自 身 发 生 了 变 化 Java 从 过 去 到 未 来 都 是 一 种 设 计 保 守 的 语 言 这 也 是 Java 的 一 大 优 势 当 James 解 释 他 设 计 Java 的 初 衷 时 说 : 当 我 在 设 计 的 时 候, 我 知 道 人 们 想 要 自 动 内 存 管 理, 人 们 想 要 強 型 式, 但 是 这 些 功 能 会 吓 跑 蓝 领 工 人 比 如 说 Smalltalk, 这 是 一 门 很 优 秀 的 语 言, 但 是 它 太 先 进 了, 它 和 现 实 中 开 发 者 们 在 构 建 应 用 时 的 思 维 脱 离 开 来 所 以 Java 继 承 了 其 中 的 一 些 理 念, 并 将 其 简 化, 把 这 些 理 念 放 入 一 种 语 言 和 格 式 中 这 些 事 解 释 了 这 门 语 言 设 计 的 基 本 动 机 所 以 你 当 然 可 以 说 Java 是 一 种 冗 长 的 语 言, 但 我 认 为 额 外 的 内 容 是 为 了 方 便 阅 读 特 别 是 当 你 还 是 一 位 初 级 或 中 级 程 序 员 的 时 候, 那 些 看 似 多 余 的 文 字 能 够 帮 助 到 你 人 们 永 远 都 铭 记 我 们 对 于 生 产 力 的 需 求 越 来 越 高, 但 是 代 码 仍 然 是 写 出 来 的 所 以 我 不 认 为 Java 冗 长, 虽 然 我 们 可 以 加 入 一 些 高 级 功 能, 但 是 有 些 事 永 远 都 无 法 在 一 个 语 言 中 改 变, 这 很 遗 憾 当 然 我 们 也 会 进 步, 但 是 就 像 我 总 说 的 一 句 话, 人 们 总 是 过 于 关 心 语 法, 而 不 是 能 用 语 言 来 实 现 什 么 问 : 现 在 不 少 的 大 企 业 (Paypal 等 ) 从 Java 切 换 到 Node.js,Java 在 企 业 中 的 地 位 受 到 威 胁,Java 和 Node.js 各 自 擅 长 的 领 域 是 什 么? 这 个 问 题 中 有 一 个 误 解, 事 实 上 并 没 有 出 现 大 波 公 司 弃 用 Java 转 向 Node.js 的 情 况 Paypal 中 启 用 Node.js 的 部 分 规 模 很 小,Paypal 的 大 部 分 运 行 代 码 仍 然 是 Java Node.js 参 与 的 只 是 一 个 试 点 项 目, 这 是 可 以 理 解 的,Node.js 是 一 个 有 趣 的 环 境, 其 中 也 有 一 些 有 趣 的 想 法 Node. 码 农 13

js 十 分 年 轻, 同 时, 它 也 有 很 多 严 重 的 问 题, 所 以 现 在 预 测 Node 的 未 来 发 展 还 为 时 尚 早 所 以 虽 然 各 种 开 发 者 网 站 上 有 很 多 支 持 Node 的 声 音, GitHub 上 有 很 多 有 趣 的 项 目 ( 比 如 用 它 写 Ardruino, 玩 硬 件 ), 但 是 在 所 有 生 产 环 境 下 的 产 品 中, 毫 无 疑 问,Java 拥 有 最 多 的 代 码 行 企 业 在 没 有 充 分 理 由 的 情 况 下 不 会 舍 弃 工 作 软 件, 虽 然 有 很 多 使 用 Node.js 的 创 业 者, 但 是 创 业 者 们 来 得 快, 走 得 也 快 作 为 近 些 年 来 有 趣 的 产 品 之 一 Twitter, 如 果 你 观 察 一 下 他 们 的 发 展 你 会 发 现 他 们 最 开 始 用 的 是 Ruby on Rails 三 四 年 前, 他 们 的 网 站 开 始 出 现 一 个 非 常 可 爱 的 卡 通 形 象, 失 败 鲸 这 是 一 件 很 尴 尬 的 事, 为 了 弄 明 白 到 底 发 生 了 什 么, 他 们 做 了 很 多 调 查, 在 查 看 了 Ruby 的 垃 圾 收 集 之 后, 他 们 发 现 自 己 无 能 为 力 同 时, 他 们 的 Java 试 点 项 目 获 得 了 成 功, 他 们 意 识 到 Java 能 解 决 他 们 的 扩 展 性 问 题 然 后 在 接 下 来 的 18 个 月, 他 们 使 用 了 一 些 JRuby 作 为 中 转 站, 然 后 将 整 个 系 统 改 写 成 Java 最 终 的 效 果 也 很 好, 他 们 围 绕 Java 引 入 了 新 的 服 务, 新 的 架 构 曾 几 何 时,Ruby 被 视 为 企 业 级 软 件 的 未 来, 但 现 如 今,Ruby 只 是 众 多 编 程 语 言 中 的 一 种 现 在 应 用 最 广 的 三 种 语 言 是 Java,JavaScript, 以 及 C/C++, 但 是 大 部 分 的 JavaScript 代 码 都 是 在 客 户 端, 如 果 把 这 三 种 语 言 去 掉, 其 他 语 言 的 市 场 份 额 都 非 常 小 问 : 直 到 现 在,Java 应 用 的 虚 拟 托 管 模 型 需 要 分 配 给 整 个 x86 虚 拟 机 用 来 托 管 一 个 单 独 的 JVM 实 例, 相 对 来 说 实 例 上 也 托 管 了 单 独 的 Java 应 用 这 样 的 方 法 效 率 很 低, 但 是 Java 本 地 并 不 支 持 多 租 户 虚 拟 以 及 云 计 算 配 置 幸 运 的 是, 在 社 区 里 可 以 找 到 一 些 为 了 解 决 云 计 算 问 题 而 产 生 的 多 租 户 Java 解 决 方 案, 你 认 为 哪 个 方 案 足 够 成 熟 可 以 应 用 到 生 产 环 境? 这 里 面 包 含 了 两 件 事, 把 虚 拟 和 云 以 及 多 租 户 混 在 一 起 并 不 完 全 正 确 比 如 说 在 QCon 上 海 上 有 很 多 分 享 是 关 于 docker 的 (docker 是 一 个 并 不 码 农 14

依 赖 于 虚 拟 化 的 平 台 ), 其 中 一 个 精 彩 的 分 享 来 自 Chris Swan 他 展 示 了 将 CPU 内 存 从 虚 拟 环 境 转 移 到 以 Docker 为 基 础 的 环 境 所 带 来 的 好 处, 虽 然 仍 不 够 完 善, 但 是 它 已 经 为 Java 带 来 了 额 外 的 优 势, 只 要 在 Docker 上 运 行 Java 你 就 能 感 受 到 我 们 应 该 把 云 和 虚 拟 的 关 系 梳 理 清 楚 另 外, 有 很 多 其 他 你 可 以 做 的 事, 比 如 你 可 以 建 立 多 个 JVM 主 机 但 是 这 个 问 题 真 正 在 问 的 是 多 租 户 关 于 这 个 问 题, 有 一 个 产 品 在 我 心 中 是 当 之 无 愧 的 冠 军, 那 就 是 Waratek Waratek 可 以 把 一 个 单 独 的 非 热 点 JVM 分 开, 并 在 其 中 运 行 主 机 JVM, 在 JVM 里 运 行 的 是 Java 虚 拟 多 租 户 JVC, 而 JVC 可 以 做 到 很 轻 量 级 我 认 为 Waratek 是 一 个 很 成 熟 可 以 投 入 使 用 的 产 品, 德 意 志 银 行 刚 刚 宣 布 把 自 己 的 第 一 个 工 作 JVM 挪 到 Waratek 上, 既 然 德 意 志 银 行 已 经 认 可 了 这 个 产 品, 那 么 这 个 产 品 应 该 也 值 得 你 花 时 间 研 究 一 下 问 :Java 经 常 被 拿 来 和 Scala 做 比 较, 这 两 种 语 言 的 设 计 目 的 有 什 么 不 同? 在 未 来, 这 两 种 语 言 是 否 可 能 发 展 方 向 完 全 一 致? Java 和 Scala 是 有 着 很 大 不 同 的 语 言 之 前 我 们 谈 到 过 Java 的 设 计 哲 学, 现 在 我 们 可 以 来 说 一 说 Scala 的 设 计 思 想, 以 及 它 们 之 间 有 什 么 不 同 Scala 最 初 是 一 门 来 自 学 术 界 的 语 言, 最 开 始 Martin Odersky 创 造 的 语 言 叫 做 Pizza, 那 时 候 Java 还 是 版 本 4, 这 个 时 候 Pizza 开 始 逐 渐 加 入 了 一 些 类 似 于 Java 范 型 的 功 能,Java 5 中 也 加 入 了 一 些 Pizza 的 功 能 作 为 范 型 Martin 是 一 个 很 聪 明 的 人,Scala 也 有 很 多 很 棒 的 设 计 但 是 同 时, 这 个 语 言 也 有 自 己 的 问 题 有 时 候 它 被 称 为 厨 房 水 槽 语 言, 可 见 人 们 对 这 门 语 言 又 爱 又 恨 这 个 比 喻 的 意 思 是 : 水 槽 里 面 装 了 各 种 各 样 数 量 过 多 的 东 西 这 确 实 是 Scala 的 一 个 问 题, 它 的 功 能 太 多 了 有 一 条 语 言 设 计 的 准 则, 同 时 也 是 Java 设 计 过 程 中 的 一 个 重 要 原 则 保 守 具 体 说 来, 就 是 每 当 你 添 加 一 个 新 特 性 的 时 候 ( Java 程 序 员 修 炼 之 道 14 页 码 农 15

谈 到 了 了 一 个 具 体 的 例 子 ), 可 能 你 也 造 成 了 新 的 问 题 如 果 你 的 语 言 有 200 种 特 性, 而 这 个 时 候 你 想 再 加 入 一 个, 我 需 要 检 验 它 和 所 有 其 他 特 性 的 交 互 情 况 对 于 Scala 来 说, 它 总 是 频 繁 地 加 入 新 的 特 性 要 想 知 道 这 些 特 性 之 间 的 交 互 情 况 是 很 困 难 的 就 算 Scala 有 一 个 很 灵 活, 能 够 拥 抱 改 变 的 社 区, 语 言 特 性 的 变 动 也 是 件 不 容 易 的 事 所 以 你 会 发 现 虽 然 Scala 拥 有 很 多 优 秀 的 工 作 性 能, 但 是 你 需 要 决 定 哪 些 特 性 是 你 想 要 的, 而 哪 些 特 性 是 你 不 能 碰 的 当 你 在 团 队 中 编 程 的 时 候, 这 不 是 个 问 题 真 正 的 问 题 在 于, 现 代 社 会 的 软 件 栈 从 来 都 不 是 仅 仅 依 赖 于 代 码, 问 题 来 自 于 函 数 库 有 一 些 Scala 特 性 的 动 作 不 仅 影 响 目 标 对 象, 还 会 影 响 其 他 一 些 东 西 Scala 的 特 性 越 多, 这 些 问 题 就 更 容 易 互 相 重 叠 另 外, 他 们 一 直 都 纠 结 于 二 进 制 兼 容 的 问 题 Java Sun 以 及 Oracle 一 直 都 认 为 这 是 对 Java 来 说 最 重 要 的 设 计 理 念, 所 以 我 可 以 用 Java 1.0 写 程 序, 编 译 一 下, 放 到 Java 8 的 虚 拟 机 中, 仍 然 可 以 运 行, 而 且 运 行 速 度 会 比 以 前 快 很 多 倍 而 Scala 从 未 做 出 这 方 面 的 承 诺, 哪 怕 就 是 上 一 个 版 本 也 会 出 现 问 题 在 函 数 库 空 间 中, 这 个 问 题 就 更 严 重 了, 我 知 道 很 多 项 目 都 放 弃 了 Scala, 就 是 因 为 每 次 只 要 升 级 函 数 库, 整 个 系 统 就 会 崩 溃 所 以 说, 这 两 种 语 言 的 设 计 思 想 很 不 相 同 人 们 总 是 喜 欢 新 鲜 事 物, 第 一 个 尝 鲜 的 人 也 会 第 一 个 享 受 到 很 多 好 处, 但 是 在 更 多 的 情 况 下, 人 们 更 愿 意 做 第 二 个 尝 试 的 人 你 可 以 观 察 第 一 个 人 犯 下 的 错 误, 然 后 从 中 学 习 而 Java 就 是 这 样 一 个 从 别 人 的 错 误 中 学 习 的 语 言 我 刚 才 提 到 过 程 序 员 的 金 字 塔, 我 认 为 Scala 并 不 适 用 于 底 层, 它 的 作 用 更 多 在 于 为 最 顶 层 的 程 序 员 们 激 发 思 考 而 Java 是 一 种 适 用 于 整 个 金 字 塔 的 语 言, 而 且 它 对 底 层 和 中 层 的 程 序 员 尤 其 适 用 我 相 信 在 未 来 的 很 多 年 内 都 会 有 一 个 强 大 且 健 康 的 Scala 社 区, 我 也 希 望 能 和 他 们 一 起 交 换 思 想 但 是 我 并 不 认 为 Scala 会 从 一 种 小 众 语 言 成 长 成 一 种 大 众 语 言 现 在 地 球 上 可 能 有 上 百 个 Scala 程 序 员, 但 是 这 个 数 量 顶 多 也 就 是 Java 程 序 员 的 百 分 之 一, 而 这 个 比 例 很 可 能 不 会 继 续 增 长 了 码 农 16

专 题 : 进 击 的 Java 如 何 在 Android 上 编 写 高 效 的 Java 代 码 Java 平 台 一 般 有 三 个 版 本 :Java ME( 微 型 版, 用 于 某 些 手 机 ) Java SE( 标 准 版, 用 于 台 式 电 脑 ) Java EE( 企 业 版, 用 于 服 务 器 端 应 用 ) 在 谈 到 Java 时, 我 们 通 常 是 指 Java SE, 因 为 只 有 这 个 版 本 包 含 虚 拟 机 和 编 译 器 作 者 / Erik Hellman Factor10 咨 询 公 司 资 深 移 动 开 发 顾 问, 曾 任 索 尼 公 司 Android 团 队 首 席 架 构 师, 主 导 Xperia 系 列 产 品 开 发 ; 精 通 移 动 应 用 Web 技 术 云 计 算 和 三 维 图 形, 定 期 在 DroidCon JFokus JavaOne 和 其 他 专 业 开 发 人 员 大 会 上 发 表 演 讲 关 于 Erik 的 更 多 信 息, 可 访 问 他 的 博 客 http://blog.hellsoft.se 首 先,Java 代 码 会 被 编 译 成 称 为 字 节 码 的 中 间 格 式 当 字 节 码 在 目 标 电 脑 上 运 行 时, 虚 拟 机 会 快 速 将 它 解 析 成 目 标 电 脑 硬 件 和 操 作 系 统 所 需 要 的 本 机 格 式 除 了 为 开 发 者 提 供 一 次 编 写, 到 处 运 行 的 优 势,Java 还 能 通 过 垃 圾 回 收 器 (GC) 实 现 自 动 内 存 管 理, 开 发 者 可 免 去 手 动 在 代 码 中 释 放 无 用 对 象 的 内 存 虽 然 这 个 功 能 非 常 有 用, 且 大 大 降 低 了 在 代 码 中 引 入 内 存 问 题 的 风 险, 但 是 它 会 增 加 运 行 时 的 开 销, 因 为 需 要 不 停 地 执 行 垃 圾 回 收 进 程 本 文 开 头 将 比 较 Java SE 和 用 于 Android 开 发 的 Java 之 间 的 差 异 首 先 我 会 介 绍 开 发 者 习 惯 的 Java SE 语 言 结 构 以 及 它 们 是 如 何 在 Android 上 运 行 的 其 次, 我 会 介 绍 如 何 优 化 Android 中 的 Java 代 码, 如 何 优 化 内 存 分 配, 以 及 如 何 恰 当 地 处 理 多 线 程 码 农 17

比 较 Android 上 的 Dalvik Java 和 Java SE 虽 然 远 在 Android 出 现 之 前, 开 发 者 就 能 用 Java 编 程 语 言 为 移 动 设 备 编 写 应 用 程 序, 但 它 只 是 Java 中 功 能 极 为 有 限 的 一 个 版 本, 称 为 Java ME ( 微 型 版 ) 不 同 的 移 动 设 备 还 需 编 写 不 同 的 代 码, 因 此, 写 一 个 应 用 程 序 就 能 在 支 持 Java ME 的 任 何 手 机 上 运 行 是 几 乎 不 可 能 的 此 外, 由 于 当 时 不 存 在 很 好 的 在 线 商 店, 应 用 发 布 过 程 极 其 复 杂 Android 的 问 世 为 开 发 者 提 供 了 构 建 智 能 手 机 强 大 应 用 的 机 会, 开 发 者 只 需 用 Java 编 程 语 言 以 及 他 们 熟 知 的 标 准 Java API 编 写 代 码 然 而, 尽 管 Android 开 发 者 仍 使 用 Java SE 编 译 器 来 编 译 应 用 程 序, 你 会 发 现,James Gosling 开 发 的 Java 和 Android 设 备 上 的 Java 存 在 许 多 不 同 之 处 在 Android 设 备 上 运 行 的 VM( 虚 拟 机 ) 称 为 Dalvik 它 最 初 由 谷 歌 的 Dan Bornstein 开 发, 适 用 于 CPU 和 内 存 受 限 的 移 动 设 备 Java SE 和 Dalvik Java 存 在 一 些 差 异, 主 要 体 现 在 虚 拟 机 上 Java SE 使 用 了 栈 机 设 计, 而 Dalvik 被 设 计 成 了 基 于 寄 存 器 的 机 器 Android SDK 中 有 一 个 dx 工 具, 它 会 把 Java SE 栈 机 器 的 字 节 码 转 换 成 基 于 寄 存 器 的 Dalvik 机 器 字 节 码, 该 转 换 步 骤 由 IDE 自 动 完 成 基 于 栈 的 虚 拟 机 和 基 于 寄 存 器 的 虚 拟 机 的 定 义 以 及 差 异 将 不 列 入 我 们 的 讨 论 范 围 由 于 历 史 原 因,Android 使 用 基 于 寄 存 器 的 虚 拟 机 虽 然 基 于 寄 存 器 的 虚 拟 机 最 多 可 以 比 基 于 栈 的 虚 拟 机 快 32%, 但 这 只 限 于 执 行 时 解 释 字 节 码 的 虚 拟 机 ( 也 就 是 说, 解 释 型 虚 拟 机 ) 在 Android 2.2 版 本 ( 也 称 为 Froyo) 之 前,Dalvik 虚 拟 机 都 是 纯 解 释 型 的 Froyo 版 本 引 入 了 JIT 编 译 器 ( 即 时 编 译 ), 这 是 Java SE 很 早 就 有 的 一 个 优 势 JIT 编 译, 也 称 为 动 态 翻 译 它 在 执 行 前 把 字 节 码 翻 译 成 本 机 代 码 ( 如 图 码 农 18

1 所 示 ), 这 样 主 要 有 两 个 好 处 首 先, 它 消 除 了 那 些 纯 解 释 型 虚 拟 机 的 开 销 ; 其 次, 它 能 对 本 机 代 码 执 行 优 化, 这 通 常 是 静 态 编 译 代 码 无 法 做 到 的 例 如,JIT 编 译 器 可 以 在 它 运 行 的 CPU 上 选 择 最 合 适 的 优 化, 也 可 以 根 据 应 用 程 序 的 输 入 来 分 析 代 码 是 如 何 运 行 的, 以 便 进 行 下 一 步 的 优 化 图 1 Android Java 和 Java SE 翻 译 步 骤 码 农 19

虽 然 Android 的 Dalvik JIT 编 译 器 有 很 大 的 发 展 前 景, 但 要 达 到 如 Java SE 的 JIT 编 译 器 般 稳 定 成 熟 度 尚 需 很 长 一 段 时 间 不 过,Dalvik JIT 的 出 现 为 Android 提 供 了 巨 大 的 性 能 优 势, 而 且 它 也 在 不 断 得 以 改 善 JAVA SE 虚 拟 机 和 Dalvik 虚 拟 机 的 另 一 个 区 别 是, 后 者 进 行 了 优 化, 可 运 行 在 同 一 个 机 器 上 的 多 个 实 例 中 它 在 开 机 时 会 启 动 一 个 叫 做 zygote 的 进 程, 该 进 程 会 创 建 第 一 个 Dalvik 实 例, 由 这 个 实 例 创 建 所 有 其 他 的 实 例 当 应 用 程 序 启 动 时,zygote 进 程 会 收 到 一 个 创 建 新 虚 拟 机 实 例 的 请 求, 并 给 该 应 用 程 序 创 建 一 个 新 进 程 ( 如 图 2 所 示 ) 如 果 开 发 者 已 习 惯 于 Java SE 开 发, 这 样 的 设 计 可 能 看 起 来 不 切 实 际, 但 它 有 一 个 很 大 的 优 势, 可 以 避 免 由 一 个 应 用 程 序 运 行 失 败 导 致 Dalvik 虚 拟 机 崩 溃, 继 而 引 发 多 应 用 程 序 崩 溃 图 2 在 Android 中 启 动 新 Dalvik 虚 拟 机 实 例 码 农 20

Android 和 Java SE 除 了 运 行 的 虚 拟 机 不 同 之 外, 它 们 实 现 API 的 方 式 也 不 一 样 Android 中 属 于 java 和 javax 包 中 的 API 都 来 自 Apache Harmony( 这 是 一 个 开 源 项 目, 旨 在 重 新 实 现 Java SE 软 件 栈, 该 项 目 从 2011 年 11 月 不 再 维 护 ) 在 开 发 方 面, 这 些 API 和 Java SE 包 中 的 类 似, 但 也 存 在 一 些 差 别 例 如, 谷 歌 对 HttpUrlConnection 类 进 行 了 Java SE 版 本 中 所 没 有 的 重 大 升 级 此 外,Android 平 台 移 除 了 Java SE 中 无 关 的 API 例 如,Swing/AWT 包 被 完 全 移 除, 因 为 Android 使 用 不 同 的 UI 框 架 其 他 被 移 除 的 API 还 有 RMI CORBA ImageIO 和 JMX 它 们 或 者 被 替 换 为 特 定 的 Android 版 本 ( 在 android 包 空 间 内 ), 或 者 因 为 一 些 实 际 原 因 根 本 不 存 在 优 化 Android 上 的 Java 代 码 经 过 多 年 的 改 进,Java SE 具 备 了 一 些 简 化 编 写 复 杂 代 码 结 构 的 新 特 性 其 中 的 一 些 特 性 会 让 整 个 流 程 变 得 更 简 单, 但 开 发 者 需 要 了 解 何 时 以 及 如 何 正 确 地 使 用 它 们 另 外, 由 于 Java SE 大 多 用 于 服 务 器 端 开 发 ( 使 用 Java 企 业 版 的 API), 因 而 开 发 人 员 专 门 对 服 务 器 端 Java 代 码 进 行 了 优 化 注 解 和 Java 虚 拟 机 对 脚 本 语 言 的 支 持 就 是 对 服 务 器 端 开 发 进 行 优 化 的 例 证 虽 然 这 些 工 具 在 构 建 后 端 开 发 时 很 强 大, 但 在 开 发 Android 客 户 端 代 码 时, 这 些 特 性 的 作 用 很 小, 甚 至 起 反 作 用 Java 开 发 者 已 经 习 惯 于 无 限 量 的 RAM 和 CPU, 而 Android 开 发 需 要 密 切 关 注 性 能 和 内 存 分 配 简 单 地 说, 开 发 者 需 要 使 用 稍 微 不 同 的 方 法 对 待 Android 和 后 端 的 开 发 然 而, 随 着 Android 的 首 次 发 布, 情 况 有 所 改 变 曾 经 一 些 在 Android 上 尽 量 不 用 的 Java 规 范 重 新 被 推 荐, 这 主 要 因 为 Android 目 前 的 JIT 编 译 器 解 决 了 这 些 规 范 导 致 的 性 能 问 题 码 农 21

本 文 将 讨 论 编 写 Android 应 用 程 序 需 要 了 解 的 Java 代 码 我 们 不 会 深 究 Java 编 程 语 言 的 细 节, 而 是 重 点 关 注 对 Android 开 发 重 要 的 东 西 不 过, 开 发 者 仍 需 了 解, 大 多 数 适 用 于 Java SE 的 规 则 和 建 议 同 样 适 用 于 Android 和 Dalvik 虚 拟 机 Android 上 的 类 型 安 全 枚 举 Java SE 5.0 新 增 了 许 多 方 便 开 发 者 的 新 特 性 其 中 最 值 得 期 待 的 是 引 入 了 类 型 安 全 枚 举 枚 举 在 代 码 中 用 来 表 示 属 于 某 一 组 的 几 个 选 择 在 早 期 版 本 的 Java 中, 可 以 用 多 个 整 型 常 量 解 决 这 个 问 题 虽 然 这 在 技 术 上 可 行, 但 是 很 容 易 出 错 请 看 下 面 的 代 码 : public class Machine { public static final int STOPPED = 10; public static final int INITIALIZING = 20; public static final int STARTING = 30; public static final int RUNNING = 40; public static final int STOPPING = 50; public static final int CRASHED = 60; private int mstate; public Machine() { mstate = STOPPED; public int getstate() { return mstate; public void setstate(int state) { mstate = state; 码 农 22

问 题 是, 虽 然 这 些 常 量 是 期 望 的, 但 是 没 有 机 制 保 证 setstate() 方 法 接 收 不 同 的 值 如 果 要 在 设 置 方 法 中 添 加 检 查, 那 么 一 旦 得 到 的 是 非 预 期 值, 开 发 者 就 需 要 处 理 错 误 开 发 者 所 需 要 的 是 在 编 译 时 检 查 非 法 赋 值 类 型 安 全 的 枚 举 解 决 了 这 个 问 题, 如 下 所 示 : public class Machine { public enum State { STOPPED, INITIALIZING, STARTING, RUNNING, STOPPING, CRASHED private State mstate; public Machine() { mstate = State.STOPPED; public State getstate() { return mstate; public void setstate(state state) { mstate = state; 注 意 在 声 明 不 同 类 型 安 全 值 的 地 方 新 加 的 内 部 枚 举 类 这 在 编 译 时 就 会 解 决 非 法 赋 值 的 问 题, 所 以 代 码 更 不 容 易 出 错 如 果 Dalvik 虚 拟 机 还 没 有 JIT 编 译 器 优 化 代 码, 不 建 议 在 Android 平 台 上 使 用 枚 举 类 型, 因 为 和 使 用 整 型 常 量 相 比, 这 种 设 计 带 来 的 内 存 和 性 能 损 失 更 大 这 就 是 为 什 么 在 一 些 老 版 本 的 Android API 中 还 存 在 如 此 多 的 整 型 常 量 的 原 因 如 今 有 了 更 强 的 JIT 编 译 器 以 及 一 个 不 断 改 进 的 Dalvik 虚 拟 机, 开 发 者 不 必 再 担 心 这 个 问 题, 放 心 大 胆 地 使 用 类 型 安 全 枚 举 即 可 码 农 23

然 而, 仍 然 存 在 一 些 情 况 使 用 整 型 常 量 是 更 好 的 选 择 像 int 这 样 的 Java 基 本 类 型, 不 会 增 加 GC 的 开 销 此 外,Android SDK 中 许 多 已 有 的 API 仍 然 依 赖 基 本 类 型, 比 如 Handler 类 在 这 种 情 况 下, 你 没 有 太 多 的 选 择 Android 中 增 强 版 的 for 循 环 Java SE 5.0 还 引 入 了 增 强 版 的 for 循 环, 提 供 了 一 个 通 用 的 缩 写 表 达 式 来 遍 历 集 合 和 数 组 首 先, 比 较 以 下 五 种 方 法 : void loopone(string[] names) { int size = names.length; for (int i = 0; i < size; i++) { printname(names[i]); void looptwo(string[] names) { for (String name : names) { printname(name); void loopthree(collection<string> names) { for (String name : names) { printname(name); void loopfour(collection<string> names) { Iterator<String> iterator = names.iterator(); while (iterator.hasnext()) { printname(iterator.next()); 码 农 24

// 不 要 在 ArrayList 上 使 用 增 强 版 的 for 循 环 void loopfive(arraylist<string> names) { int size = names.size(); for (int i = 0; i < size; i++) { printname(names.get(i)); 上 面 显 示 了 四 种 不 同 遍 历 集 合 和 数 组 的 方 式 前 面 两 种 有 着 相 同 的 性 能, 所 以 如 果 只 是 读 取 元 素 的 话, 可 以 放 心 地 对 数 组 使 用 增 强 版 for 循 环 对 Collection 对 象 来 说, 增 强 版 for 循 环 和 使 用 迭 代 器 遍 历 元 素 有 着 相 同 的 性 能 ArrayList 对 象 应 避 免 使 用 增 强 版 for 循 环 如 果 不 仅 需 要 遍 历 元 素, 而 且 需 要 元 素 的 位 置, 就 一 定 要 使 用 数 组 或 者 ArrayList, 因 为 所 有 其 他 Collection 类 在 这 些 情 况 下 会 更 慢 一 般 情 况 下, 如 果 在 读 取 元 素 几 乎 不 变 的 数 据 集 时 对 性 能 要 求 很 高, 建 议 使 用 常 规 数 组 然 而, 数 组 的 大 小 固 定, 添 加 数 据 会 影 响 性 能, 所 以 编 写 代 码 时 要 考 虑 所 有 因 素 队 列 同 步 和 锁 通 常 情 况 下, 应 用 程 序 会 在 一 个 线 程 中 生 产 数 据, 在 另 一 个 线 程 中 使 用 它 们 常 见 的 例 子 是 在 一 个 线 程 中 获 取 网 络 上 的 数 据, 在 另 一 个 线 程 ( 操 作 UI 的 主 线 程 ) 中 把 这 些 数 据 展 现 给 用 户 这 种 模 式 称 为 生 产 者 / 消 费 者 模 式, 在 面 向 对 象 编 程 课 程 中, 开 发 者 用 算 法 来 实 现 该 模 式 可 能 要 花 上 几 个 小 时 下 面 会 介 绍 一 些 简 化 生 产 者 / 消 费 者 模 式 实 现 的 现 成 类 1. 更 智 能 的 队 列 虽 然 已 有 现 成 的 类 并 能 用 更 少 的 代 码 实 现 该 功 能, 但 许 多 Java 开 发 者 仍 然 选 择 使 用 LinkedList 以 及 同 步 块 实 现 队 列 功 能 开 发 者 可 在 码 农 25

java.util.concurrent 包 中 找 到 同 步 相 关 的 类 此 外, 本 包 还 包 含 信 号 量 锁 以 及 对 单 个 变 量 进 行 原 子 操 作 的 类 考 虑 下 面 使 用 标 准 的 LinkedList 实 现 线 程 安 全 队 列 的 代 码 public class ThreadSafeQueue { private LinkedList<String> mlist = new LinkedList<String>(); private final Object mlock = new Object(); public void offer(string value) { synchronized (mlock) { mlist.offer(value); mlock.notifyall(); public synchronized String poll() { synchronized (mlock) { while (mlist.isempty()) { try { mlock.wait(); catch (InterruptedException e) { // 简 洁 起 见 忽 略 异 常 处 理 return mlist.poll(); 虽 然 这 段 代 码 是 正 确 的, 并 有 可 能 在 考 试 中 得 满 分, 但 实 现 和 测 试 这 样 一 段 代 码 只 是 在 浪 费 时 间 实 际 上, 所 有 前 面 的 代 码 可 用 下 面 一 行 代 替 LinkedBlockingQueue<String> blockingqueue = new LinkedBlockingQueue<String>(); 码 农 26

上 面 的 一 行 代 码 能 像 前 面 的 例 子 一 样 提 供 相 同 类 型 的 阻 塞 队 列, 甚 至 能 提 供 额 外 的 线 程 安 全 操 作 java.util.concurrent 包 含 许 多 可 选 的 队 列 以 及 并 发 映 射 类, 所 以, 一 般 情 况 下, 建 议 使 用 它 们, 而 不 是 像 之 前 的 示 例 那 样 使 用 更 多 代 码 2. 更 智 能 的 锁 Java 提 供 的 synchronized 关 键 字 允 许 开 发 者 创 建 线 程 安 全 的 方 法 和 代 码 块 synchronized 关 键 字 易 于 使 用, 也 很 容 易 滥 用, 对 性 能 造 成 负 面 影 响 当 需 要 区 分 读 数 据 和 写 数 据 时,synchronized 关 键 字 并 不 是 最 有 效 的 幸 好,java.util.concurrent.locks 包 中 的 工 具 类 对 这 种 情 况 提 供 了 很 好 的 支 持 public class ReadWriteLockDemo { private final ReentrantReadWriteLock mlock; private String mname; private int mage; private String maddress; public ReadWriteLockDemo() { mlock = new ReentrantReadWriteLock(); public void setpersondata(string name, int age, String address) { ReentrantReadWriteLock.WriteLock writelock = mlock. writelock(); try { writelock.lock(); mname = name; mage = age; maddress = address; finally { writelock.unlock(); 码 农 27

public String getname() { ReentrantReadWriteLock.ReadLock readlock = mlock. readlock(); try { readlock.lock(); return mname; finally { readlock.unlock(); // 重 复 代 码 不 再 赘 述 上 面 的 代 码 展 示 了 在 什 么 地 方 使 用 ReentrantReadWriteLock, 它 允 许 多 个 并 发 线 程 对 数 据 进 行 只 读 访 问, 并 确 保 同 一 时 间 只 有 一 个 线 程 写 入 相 同 的 数 据 在 代 码 中 使 用 synchronized 关 键 字 仍 然 是 处 理 锁 问 题 的 有 效 方 法, 但 无 论 何 种 情 况 下, 都 要 考 虑 ReentrantReadWriteLock 是 否 是 更 有 效 的 解 决 方 案 管 理 和 分 配 内 存 Java 中 的 自 动 内 存 管 理 有 效 消 除 了 软 件 开 发 过 程 中 许 多 最 常 见 的 问 题 当 不 再 需 要 记 住 为 每 个 新 建 的 对 象 释 放 内 存 时, 开 发 者 可 以 用 省 下 的 时 间 改 善 功 能 以 及 软 件 的 整 体 质 量 但 需 要 为 这 个 功 能 付 出 代 价, 因 为 自 动 垃 圾 回 收 器 会 和 应 用 程 序 并 行 运 行 垃 圾 回 收 器 会 一 直 运 行, 并 检 查 是 否 有 可 以 回 收 的 内 存 这 种 行 为 意 味 着 应 用 程 序 进 程 会 和 垃 圾 回 收 器 竞 争 CPU 时 间, 所 以 至 关 重 要 的 一 点 是, 确 保 垃 圾 回 收 器 不 管 何 时 运 行 都 不 会 占 用 太 长 时 间 码 农 28

此 外, 自 动 内 存 管 理 并 不 能 保 证 不 会 有 内 存 泄 漏 如 果 引 用 了 不 再 需 要 的 对 象, 垃 圾 回 收 器 不 会 收 集 它 们, 这 将 导 致 内 存 的 浪 费 如 果 一 直 分 配 对 象, 但 从 不 释 放, 最 终 会 导 致 OutOfMemory 异 常, 应 用 程 序 也 会 崩 溃 所 以, 要 尽 量 避 免 在 Android 的 主 要 组 件 中 引 用 对 象, 否 则, 这 些 对 象 在 应 用 程 序 的 生 命 周 期 中 可 能 永 远 不 会 被 垃 圾 回 收 减 少 对 象 分 配 Java 和 Android 中, 自 动 内 存 管 理 最 常 见 的 问 题 是 分 配 了 无 用 的 对 象, 导 致 垃 圾 回 收 器 一 直 运 行 考 虑 一 种 情 况, 一 个 代 表 一 对 整 数 的 简 单 类 : public final class Pair { public int firstvalue; public int secondvalue; public Pair(int firstvalue, int secondvalue) { this.firstvalue = firstvalue; this.secondvalue = secondvalue; 现 在, 假 如 在 应 用 程 序 中 接 收 了 一 个 整 数 数 组, 把 它 们 进 行 分 组, 然 后 使 用 sendpair 方 法 下 面 是 一 个 内 存 分 配 做 得 很 差 的 例 子 : public void badobjectallocationexample(int[] pairs) { if(pairs.length % 2!= 0) { throw new IllegalArgumentException( Bad array size! ); for(int i = 0; i < pairs.length; i+=2) { Pair pair = new Pair(pairs[i], pairs[i+1]); sendpair(pair); 码 农 29

虽 然 这 是 个 展 示 如 何 生 成 Pair 对 象 的 简 单 粗 糙 的 例 子 ( 如 果 数 组 的 大 小 是 奇 数 的 话 可 能 会 引 起 崩 溃 ), 但 它 说 明 了 一 个 很 常 见 的 错 误 : 在 循 环 中 分 配 对 象 在 上 面 的 循 环 中, 垃 圾 回 收 器 将 会 做 很 多 工 作, 并 很 可 能 耗 尽 CPU 从 而 导 致 应 用 程 序 用 户 界 面 卡 顿 如 果 开 发 者 知 道 sendpair 方 法 返 回 时 并 不 会 持 有 Pair 对 象 的 引 用, 那 么 解 决 方 案 很 简 单, 在 循 环 外 创 建 Pair 对 象 并 重 用, 如 下 所 示 : public void betterobjectallocationexample(int[] pairs) { if(pairs.length % 2!= 0) { throw new IllegalArgumentException ("Bad array size!"); Point thepair = new Point(0,0); for (int i = 0; i < pairs.length; i+=2) { thepair.set(pairs [i], pairs [i+1]); sendpair(thepair); 新 版 的 方 法 确 保 了 在 整 个 运 行 过 程 中 一 直 重 用 该 对 象 当 方 法 返 回 时, 只 会 有 一 次 垃 圾 回 收 请 记 住, 应 尽 可 能 避 免 在 循 环 中 分 配 对 象 然 而 有 时 候 无 法 避 免 在 循 环 中 创 建 对 象, 所 以 还 需 要 采 用 某 种 方 法 处 理 这 种 情 况 我 们 的 解 决 方 案 是 使 用 一 个 静 态 工 厂 方 法 按 需 分 配 对 象, Joshua Bloch 在 Effective Java 中 文 版 一 书 的 第 一 条 中 详 细 地 描 述 了 该 方 法 这 种 方 法 在 Android 框 架 和 API 中 很 常 见, 它 允 许 开 发 者 使 用 一 个 按 需 填 充 的 对 象 缓 存 唯 一 的 缺 点 是 需 要 手 动 回 收 这 些 对 象, 否 则 缓 存 会 一 直 是 空 的 基 于 前 面 的 例 子, 通 过 重 构 Pair 类 来 使 用 一 个 简 单 的 对 象 池 重 用 对 象 public final class Pair { public int firstvalue; 码 农 30

public int secondvalue; // 引 用 对 象 池 中 的 下 一 个 对 象 private Pair next; // 同 步 锁 private static final Object spoolsync = new Object(); // 对 象 池 中 第 一 个 可 用 的 对 象 private static Pair spool; private static int spoolsize = 0; private static final int MAX_POOL_SIZE = 50; /** * 只 能 用 obtain() 方 法 获 取 对 象 */ private Pair() { /** * 返 回 回 收 的 对 象 或 者 当 对 象 池 为 空 时 创 建 一 个 新 对 象 */ public static Pair obtain() { synchronized (spoolsync) { if (spool!= null) { Pair m = spool; spool = m.next; m.next = null; spoolsize--; return m; return new Pair(); /** * 回 收 该 对 象 调 用 该 方 法 后 需 要 释 放 所 有 对 该 实 例 的 引 用 */ 码 农 31

public void recycle() { synchronized (spoolsync) { if (spoolsize < MAX_POOL_SIZE) { next = spool; spool = this; spoolsize++; 注 意, 本 例 增 加 了 多 个 字 段, 有 静 态 的 也 有 非 静 态 的 可 使 用 这 些 字 段 实 现 传 统 的 Pair 对 象 链 表 只 能 通 过 obtain 方 法 创 建 该 类 的 对 象 通 过 使 用 私 有 构 造 函 数 来 防 止 在 类 外 面 创 建 对 象 obtain 方 法 首 先 会 检 查 对 象 池 中 是 否 包 含 任 何 存 在 的 对 象, 并 删 除 列 表 中 的 第 一 个 元 素 然 后 返 回 它 如 果 对 象 池 为 空,obtain 方 法 会 创 建 一 个 新 的 对 象 要 把 对 象 重 新 放 回 池 中, 需 要 在 使 用 完 该 对 象 时, 对 它 调 用 recycle 方 法 这 时, 不 能 再 有 对 该 对 象 的 引 用 修 改 Pair 类 后, 之 前 的 循 环 方 法 也 需 要 修 改 public void bestobjectallocationexample(int[] pairs) { if (pairs.length % 2!= 0) throw new IllegalArgumentException("Bad array size!"); for (int i = 0; i < pairs.length; i += 2) { Pair pair = Pair.obtain(); pair.firstvalue = pairs[i]; pair.secondvalue = pairs[i + 1]; sendpair(pair); pair.recycle(); 码 农 32

第 一 次 运 行 这 个 方 法 会 创 建 一 个 新 的 Pair 实 例, 接 下 来 的 每 次 迭 代 会 重 用 改 对 象 不 过, 下 次 再 运 行 该 方 法 时, 不 会 再 创 建 新 的 对 象 另 外, 由 于 obtain 和 recycle 是 线 程 安 全 的, 可 以 在 多 个 并 发 线 程 中 安 全 地 使 用 这 两 个 方 法 唯 一 的 缺 点 是, 必 须 记 住 要 手 动 调 用 recycle 方 法, 不 过 这 是 一 个 很 小 的 代 价 这 样 就 只 会 在 应 用 退 出 时 才 会 对 Pair 类 进 行 垃 圾 回 收 Pair 类 的 例 子 很 琐 碎, 但 是 它 描 述 了 一 个 模 式 能 明 显 减 少 类 的 创 建 这 个 设 计 可 能 看 起 来 很 熟 悉, 因 为 它 出 现 在 Android 源 代 码 和 API 的 多 个 地 方 一 些 经 常 使 用 的 类, 比 如 Message MotionEvent 以 及 Parcel 都 通 过 实 现 这 个 模 式 来 减 少 不 必 要 的 垃 圾 回 收 之 前 的 Pair 类 基 本 上 就 是 复 制 Message 类 的 实 现 使 用 这 种 方 法 时 记 得 在 使 用 完 对 象 后 调 用 recycle 方 法, 否 则 对 象 池 将 一 直 是 空 的 Android 中 的 多 线 程 编 程 中 最 难 的 部 分 之 一 编 写 在 多 个 线 程 中 执 行 的 代 码 这 是 对 当 今 应 用 的 一 个 要 求, 因 为 不 可 能 只 在 一 个 线 程 中 按 顺 序 执 行 所 有 代 码 Android 应 用 程 序 从 主 线 程 开 始 运 行, 也 称 为 UI 线 程 ( 这 里 UI 线 程 和 主 线 程 含 义 相 同 ) 除 非 启 动 另 一 个 线 程 或 者 通 过 隐 式 调 用 函 数 来 启 动 一 个 线 程, 否 则 所 有 在 Android 应 用 中 的 操 作 都 会 运 行 在 主 线 程 中 这 意 味 着, 如 果 在 主 线 程 执 行 很 耗 时 的 操 作 ( 比 如 在 onresume 中 运 行 代 码 ), 所 有 的 绘 制 以 及 输 入 事 件 将 被 阻 塞, 直 到 该 操 作 完 成 所 以, 编 写 代 码 时 首 选 需 要 牢 记 的 是 : 确 保 永 远 不 要 阻 塞 主 线 程 但 是 怎 样 才 能 知 道 一 个 方 法 是 否 在 主 线 程 中 执 行? Android 官 方 文 档 指 出 : 默 认 情 况 下, 应 用 的 所 有 组 件 都 运 行 在 同 一 个 进 程 和 线 程 中 ( 主 线 程 ) 更 具 体 点 儿,Android 组 件 (Activity BroadcastReceiver Service 以 及 Application) 的 所 有 回 调 ( 基 本 上 是 所 有 的 onx 方 码 农 33

法 ) 都 运 行 在 主 线 程 里 因 此,Service 的 onstartcommand 方 法 和 Activity 的 onresume 也 运 行 在 同 一 个 线 程 里 需 要 记 住 的 时, 阻 塞 上 面 任 意 一 个 方 法, 都 可 能 导 致 系 统 杀 死 应 用 程 序 只 要 应 用 程 序 进 程 还 在 执 行, 主 线 程 会 一 直 运 行 通 过 使 用 Looper 类, 主 线 程 会 在 应 用 程 序 的 生 命 周 期 中 一 直 执 行 Looper 类 会 在 当 前 线 程 中 一 直 查 询 消 息 队 列 ( 使 用 MessageQueue 类 ) 对 该 队 列 的 查 询 会 被 阻 塞 直 到 有 新 的 消 息 进 入, 这 能 确 保 空 闲 时 线 程 进 入 休 眠 状 态 所 有 对 主 线 程 的 操 作 都 是 通 过 直 接 使 用 Handler 对 象 或 者 间 接 使 用 部 分 Android API ( 比 如,runOnUiThread 方 法 ) 往 队 列 里 发 送 消 息 完 成 的 可 以 通 过 Context.getMainLooper() 来 查 询 应 用 程 序 主 线 程 的 Looper 对 象 什 么 样 的 代 码 在 主 线 程 中 执 行 才 是 安 全 的? 什 么 样 的 代 码 需 要 放 到 其 他 线 程 中? 严 格 地 讲, 只 有 那 些 必 须 在 主 线 程 执 行 的 方 法 才 能 放 在 主 线 程 中 其 他 一 切 操 作 都 应 放 在 另 一 个 单 独 的 线 程 中 执 行 实 际 情 况 下, 那 些 不 会 耗 时 的 操 作 也 可 以 放 在 主 线 程 中 如 果 能 确 保 在 另 一 个 单 独 的 线 程 中 执 行 文 件 数 据 库 或 者 网 络 操 作, 通 常 主 线 程 会 是 安 全 的 另 外, 对 于 某 些 应 用 或 者 游 戏, 开 发 人 员 可 能 会 不 定 期 执 行 一 些 与 UI 无 关 的 计 算, 这 些 操 作 也 应 该 放 在 一 个 单 独 的 线 程 中 执 行 然 而, 也 要 确 保 同 一 时 间 不 会 运 行 太 多 线 程, 原 因 是 CPU 切 换 线 程 也 会 造 成 性 能 损 失 在 编 写 Android 代 码 时 如 何 声 明 和 管 理 各 种 线 程? Thread 类 Thread 类 是 Android 中 所 有 线 程 的 基 类,Java SE 中 也 包 含 它 如 果 要 在 线 程 中 执 行 代 码, 既 可 以 创 建 一 个 具 体 的 类 ( 即 继 承 自 Thread 的 新 类 ), 也 可 以 把 实 现 Runnable 接 口 的 类 对 象 传 给 Thread 的 构 造 函 数 码 农 34

本 例 需 要 遍 历 Objects 的 数 组, 从 而 把 数 据 上 传 到 服 务 器 ( 用 于 上 传 的 代 码 不 是 本 例 的 一 部 分 ) 需 要 在 一 个 单 独 的 线 程 中 执 行 此 操 作, 否 则 会 阻 塞 用 户 界 面 此 外, 需 要 通 过 增 加 ProgressBar 来 更 新 上 传 的 进 度 下 面 的 代 码 显 示 了 通 过 实 现 Runnable 接 口 来 解 决 这 个 问 题 : public class MyThread implements Runnable { private Object[] minput; private Activity mactivity; private int mprogress = 0; public MyThread(Activity activity, Object... input) { mactivity = activity; minput = input; @Override public void run() { mprogress = 0; Runnable runnable = new Runnable() { public void run() { mactivity.findviewbyid(r.id.progressbar). setvisibility(view.visible); ((ProgressBar) mactivity. findviewbyid(r.id.progressbar)). setprogress(0); ; mactivity.runonuithread(runnable); // 循 环 并 处 理 输 入 for (Object input : minput) { // 上 传 到 服 务 器 ( 用 睡 眠 模 拟 ) SystemClock.sleep(50); runnable = new Runnable() { public void run() { ((ProgressBar) mactivity. 码 农 35

findviewbyid(r.id.progressbar)). setmax(++mprogress); ((ProgressBar) mactivity. findviewbyid(r.id.progressbar)). setprogress(minput.length); ; mactivity.runonuithread(runnable); runnable = new Runnable() { public void run() { mactivity.findviewbyid(r.id.progressbar). setvisibility(view.invisible); ; mactivity.runonuithread(runnable); 从 上 面 的 例 子 可 以 看 出, 每 次 更 新 UI 都 需 要 创 建 一 个 新 的 Runnable 对 象 这 使 得 代 码 变 得 很 乱, 而 且 垃 圾 回 收 器 还 会 进 行 不 必 要 的 对 象 回 收, 这 些 都 是 开 发 者 要 避 免 的 为 了 在 主 线 程 中 使 用 runonuithread 方 法 更 新 UI, 必 须 使 用 Runnable 这 种 方 案 还 有 一 个 问 题 : 因 为 只 能 对 Thread 实 例 调 用 一 次 start 方 法, 所 以 每 次 执 行 操 作 都 需 要 创 建 一 个 新 的 Thread 对 象 不 断 创 建 新 的 线 程 是 非 常 昂 贵 的, 本 例 还 有 改 进 的 空 间 总 之, 这 不 是 一 个 非 常 灵 活 的 方 法, 开 发 者 应 避 免 直 接 使 用 Thread 类 AsyncTask AsyncTask 是 Android 中 比 较 流 行 的 几 个 类 中 的 一 个, 因 为 它 很 容 易 使 用 它 允 许 开 发 者 定 义 一 个 运 行 在 单 独 线 程 中 的 任 务, 还 能 在 任 务 的 不 码 农 36

同 阶 段 提 供 回 调 函 数 这 些 回 调 函 数 被 设 计 成 无 需 使 用 runonuithread 方 法 即 可 更 新 UI, 这 非 常 适 合 表 示 长 时 间 运 行 的 操 作 的 进 度 下 面 的 示 例 使 用 AsyncTask 来 完 成 Thread 例 子 中 的 功 能 : public class MyAsyncTask extends AsyncTask<String, Integer, Integer> { private Activity mactivity; public MyAsyncTask(Activity activity) { mactivity = activity; @Override protected void onpreexecute() { super.onpreexecute(); // 下 面 的 代 码 会 运 行 在 主 线 程 中 mactivity.findviewbyid(r.id.progressbar). setvisibility(view.visible); ((ProgressBar) mactivity.findviewbyid(r.id.progressbar)). setprogress(0); @Override protected Integer doinbackground(string... inputs) { // 下 面 的 代 码 不 会 运 行 在 主 线 程 中 int progress = 0; for (String input : inputs) { // 把 输 入 上 传 到 服 务 器 ( 用 睡 眠 代 替 ) SystemClock.sleep(50); publishprogress(++progress, inputs.length); return progress; @Override protected void onprogressupdate(integer... values) { // 下 面 的 代 码 会 运 行 在 主 线 程 中 码 农 37

((ProgressBar) mactivity.findviewbyid(r.id.progressbar)). setmax(values[1]); ((ProgressBar) mactivity.findviewbyid(r.id.progressbar)). setprogress(values[0]); @Override protected void onpostexecute(integer i) { super.onpostexecute(i); // 下 面 的 代 码 会 运 行 在 主 线 程 中 mactivity.findviewbyid(r.id.progressbar). setvisibility(view.invisible); 上 面 的 例 子 实 现 了 四 个 回 调 函 数, 并 在 代 码 注 释 中 表 明 了 它 们 会 运 行 在 哪 个 线 程 可 以 看 到,onPreExecute onprogressupdate 和 onpostexecute 方 法 都 运 行 在 主 线 程, 所 以 可 以 安 全 地 在 这 些 线 程 中 更 新 UI 每 次 触 发 onprogressupdate 回 调 函 数 都 会 调 用 publishprogress, 这 样 可 以 更 新 进 度 条 通 过 AsyncTask 类, 开 发 者 可 以 很 容 易 在 其 他 线 程 中 执 行 耗 时 的 任 务, 也 可 以 在 需 要 时 很 方 便 地 和 主 线 程 通 信 使 用 AsyncTask 唯 一 的 问 题 是 该 类 的 实 例 只 能 使 用 一 次, 这 意 味 着 每 次 执 行 操 作 都 要 新 建 一 个 MyAsyncTask 对 象 虽 然 是 个 轻 量 级 的 类 ( 实 际 的 线 程 是 由 ExecutorService 管 理 的 ), 但 它 不 适 合 那 些 频 繁 的 操 作, 因 为 这 会 快 速 聚 集 需 要 垃 圾 回 收 的 对 象, 并 最 终 导 致 应 用 程 序 界 面 卡 顿 此 外,AsyncTask 不 能 对 操 作 设 置 执 行 时 间, 也 无 法 间 隔 一 段 时 间 执 行 操 作 它 适 合 文 件 下 载, 以 及 不 会 频 繁 发 生 或 通 过 用 户 交 互 等 类 似 情 况 的 操 作 然 而, 由 于 容 易 实 现,AsyncTask 很 可 能 是 开 发 时 首 选 的 类 码 农 38

Handler 类 当 需 要 更 细 粒 度 地 控 制 在 一 个 单 独 的 线 程 中 执 行 操 作 时,Handler 类 会 是 一 个 很 有 用 的 工 具 该 类 允 许 开 发 者 准 确 地 控 制 操 作 的 执 行 时 间, 还 可 以 重 复 多 次 使 用 它 执 行 操 作 的 线 程 会 一 直 运 行, 直 到 被 显 式 地 终 止 Looper 类 会 处 理 幕 后 的 事 情, 但 开 发 者 很 少 需 要 直 接 和 它 打 交 道, 相 反 可 以 通 过 包 装 类 HandlerThread 创 建 它 下 面 的 例 子 展 示 了 如 何 在 Activity 中 创 建 一 个 Handler 实 例 public class SampleActivity extends Activity implements Handler. Callback { private Handler mhandler; @Override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); // 使 用 Looper 开 启 一 个 新 线 程 HandlerThread handlerthread = new HandlerThread( BackgroundThread ); handlerthread.start(); // 创 建 Handler 对 象 mhandler = new Handler(handlerThread.getLooper(), this); @Override protected void ondestroy() { super.ondestroy(); // 关 闭 Looper 线 程 mhandler.getlooper().quit(); @Override public boolean handlemessage(message message) { // 处 理 消 息... // 回 收 消 息 对 象 码 农 39

message.recycle(); return true; 通 过 新 建 的 Handler 对 象, 开 发 者 可 以 安 全 地 精 确 安 排 操 作 的 执 行 时 间 使 用 Handler 类 最 常 见 的 方 式 是 发 送 Message 当 向 后 台 线 程 传 递 数 据 和 参 数 时, 这 些 消 息 对 象 简 单 易 于 创 建, 并 且 可 以 重 用 Message 对 象 通 常 是 由 它 的 公 有 整 型 成 员 变 量 what 定 义 的, 可 以 在 handlemessage 回 调 函 数 中 将 其 作 为 switch-case 语 句 的 一 个 标 志 位 来 使 用 它 还 有 两 个 名 为 arg1 和 arg2 的 整 型 成 员 变 量, 它 们 用 于 创 建 低 开 销 的 参 数, 以 及 obj 成 员 变 量 ( 可 以 存 储 任 意 单 个 对 象 的 引 用 ) 如 果 需 要 的 话, 还 可 以 用 setdata(bundle obj) 方 法 设 置 更 复 杂 的 数 据 我 们 可 以 使 用 多 种 方 法 给 Handler 发 送 消 息, 下 面 列 出 最 常 见 的 三 种 : public void sendmessagedemo(object data) { // 创 建 一 个 带 有 data 参 数 的 Message, 然 后 立 刻 把 它 发 送 到 handler 执 行 Message.obtain(mHandler, SYNC_DATA, data).sendtotarget(); // 立 刻 给 handler 发 送 一 个 简 单 的 空 消 息 mhandler.sendemptymessage(sync_data); // 给 handler 发 送 一 个 简 单 的 空 消 息, 该 消 息 会 在 30 秒 后 执 行 mhandler.sendemptymessageattime(sync_data, THIRTY_SECONDS_IN_MILLISECONDS); // 给 handler 发 送 带 有 arguments 和 obj 参 数 的 消 息, 并 在 两 分 钟 后 执 行 int recipient = getrecipientid(); int priority = 5; Message msg = mhandler.obtainmessage(sync_data, recipient, priority, data); mhandler.sendmessagedelayed(msg, TWO_MINUTES_IN_ MILLISECONDS); 前 面 两 个 例 子 表 明 既 可 以 用 Message 类 也 可 以 用 Handler 对 象 创 建 和 发 送 消 息 在 第 三 个 和 第 四 个 例 子 中, 可 以 看 到 如 何 精 确 到 毫 秒 来 安 排 消 息 的 处 理 码 农 40

循 环 线 程 会 从 消 息 队 列 中 读 取 Message 对 象, 然 后 把 它 发 送 到 回 调 函 数 中 多 个 Handler 对 象 可 以 共 用 一 个 回 调 函 数, 就 像 代 理 方 法 一 样, 处 理 应 用 程 序 消 息 会 很 有 用 甚 至 可 以 在 Activity 和 Service 之 间 共 享 回 调 函 数 实 现 回 调 函 数 最 有 效 的 方 式 是 在 实 现 它 的 类 中 保 持 所 有 代 表 what 值 的 常 量, 然 后 用 标 准 的 switch-case 语 句 处 理 每 种 消 息 类 型 前 面 的 例 子 在 Activity 中 实 现 了 回 调 函 数, 但 是 使 用 一 个 单 独 的 类 并 把 应 用 程 序 的 Context 传 给 它 通 常 会 更 有 用, 因 为 这 样 就 可 以 在 应 用 程 序 的 各 个 部 分 中 使 用 它 下 面 是 一 个 典 型 的 回 调 函 数 示 例 : // 用 于 what 成 员 变 量 的 常 量 值 public static final int SYNC_DATA = 10; public static final int PING_SERVER = 20; @Override public boolean handlemessage(message message) { switch (message.what) { case SYNC_DATA: // 执 行 耗 时 的 网 络 输 入 / 输 出 操 作 syncdatawithserver(message.obj); break; case PING_SERVER: // ping 服 务 器, 应 该 定 期 执 行 pingserver(); break; // 回 收 消 息 对 象 以 便 节 省 内 存 message.recycle(); return true; 本 例 中 的 handlemessage 回 调 只 实 现 了 两 个 操 作,SYNC_DATA 和 PING_SERVER 第 一 个 可 能 会 被 用 户 事 件 触 发, 比 如, 保 存 文 件 或 者 准 备 好 将 新 数 据 上 传 到 服 务 器 第 二 个 应 该 每 间 隔 一 段 时 间 执 行 一 次 然 而,Handler 类 并 没 有 方 法 间 隔 地 发 送 消 息, 所 以 开 发 者 要 自 己 实 现 这 种 行 为 码 农 41

1. 间 隔 地 执 行 操 作 假 设 Activity 一 启 动, 就 每 分 钟 ping 一 次 服 务 器 退 出 Activity 后 停 止 执 行 ping 操 作 接 下 来 的 例 子 在 onresume() 和 onpause() 中 增 加 了 对 Handler 的 调 用 ( 前 面 有 如 何 创 建 Handler 实 例 的 例 子 ), 这 样 就 能 在 Activity 显 示 或 者 消 失 时 有 效 地 执 行 上 面 的 操 作 在 onresume 方 法 中, 把 是 否 需 要 ping 服 务 器 的 布 尔 值 设 置 成 true, 然 后 立 刻 发 送 一 个 PING_SERVER 消 息 ( 第 一 个 ping 操 作 应 尽 快 发 生 ) 消 息 会 到 达 前 面 例 子 所 述 的 回 调 函 数 中, 并 在 该 回 调 函 数 中 执 行 pingserver() 方 法 public class SampleActivity extends Activity implements Handler. Callback { private static final String PING_URL = "http://www.server. com/ping ; private static final int SIXTY_SECONDS_IN_MILLISECONDS = 60 * 1000; public static final int SYNC_DATA = 10; public static final int PING_SERVER = 20; private Handler mhandler; private boolean mpingserver = false; private int mfailedpings = 0; 简 单 起 见, 移 除 了 前 面 例 子 的 代 码 @Override protected void onresume() { super.onresume(); mpingserver = true; mhandler.sendemptymessage(ping_server); @Override 码 农 42

protected void onpause() { super.onpause(); mpingserver = false; mhandler.removemessages(ping_server); private void pingserver() { HttpURLConnection urlconnection; try { URL pingurl = new URL(PING_URL); urlconnection = (HttpURLConnection) pingurl. openconnection(); urlconnection.setrequestmethod("get"); urlconnection.connect(); if (urlconnection.getresponsecode() == 200) { mfailedpings = 0; // 这 儿 也 需 要 处 理 网 络 失 败 的 情 况 catch (IOException e) { // 还 需 要 处 理 网 络 错 误 finally { if (urlconnection!= null) urlconnection.disconnect(); if (mpingserver) { mhandler.sendemptymessagedelayed(ping_server, SIXTY_SECONDS_IN_MILLISECONDS); 在 pingserver() 方 法 中, 通 过 发 送 一 个 简 单 的 HTTP 请 求 来 看 服 务 器 是 否 还 处 在 活 动 中 一 旦 请 求 完 成, 需 要 检 查 是 否 要 继 续 ping 服 务 器, 如 果 是 的 话,60 秒 后 再 发 送 一 个 PING_SERVER 消 息 在 onpause() 方 法 中, 把 该 布 尔 值 设 置 成 false, 然 后 移 除 消 息 队 列 中 所 有 的 PING_ SERVER 消 息 码 农 43

2. 在 Handler 中 使 用 MainLooper 因 为 在 构 造 函 数 中 传 递 Looper 对 象 可 以 为 Handler 分 配 线 程, 所 以 我 们 可 以 创 建 一 个 处 理 主 线 程 消 息 的 Handler 如 果 想 避 免 使 用 runonuithread() 方 法, 这 样 做 特 别 有 用 经 常 使 用 runonuithread 会 导 致 代 码 丑 陋 且 低 效 笔 者 经 常 在 应 用 程 序 中 使 用 这 种 方 式, 这 样 就 可 以 在 主 线 程 和 后 台 线 程 之 间 简 单 地 发 送 消 息 @Override public boolean handlemessage(message message) { switch (message.what) { case SYNC_DATA: syncdatawithserver(message.obj); break; case SET_PROGRESS: ProgressBar progressbar = (ProgressBar) findviewbyid(r.id.progressbar); progressbar.setprogress(message.arg1); progressbar.setmax(message.arg2); break; message.recycle(); return true; 前 面 的 handlemessage 例 子 可 以 接 收 两 种 类 型 的 消 息,SYNC_DATA 和 SET_PROGRESS 第 一 个 需 要 运 行 在 一 个 单 独 的 线 程 中, 而 第 二 个 由 于 要 更 新 UI 需 要 运 行 在 主 线 程 中 要 做 到 这 一 点, 需 要 创 建 一 个 额 外 的 Handler 对 象 来 发 送 消 息, 以 便 主 线 程 处 理 @Override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); 码 农 44

mmainhandler = new Handler(getMainLooper(), this); HandlerThread handlerthread = new HandlerThread( BackgroundT hread ); handlerthread.start(); mhandler = new Handler(handlerThread.getLooper(), this); 需 要 注 意 的 是 本 例 的 oncreate 方 法 和 之 前 的 基 本 相 同 唯 一 例 外 的 地 方 是 创 建 mmainhandler 的 一 行 代 码 不 是 启 动 一 个 HandlerThread, 而 是 简 单 地 获 取 主 线 程 的 Looper 对 象 这 并 不 影 响 主 线 程 的 运 行, 只 需 要 一 个 额 外 的 Handler, 然 后 在 主 线 程 处 理 回 调 系 统 会 在 回 调 函 数 中 处 理 发 送 给 该 Handler 的 消 息, 该 回 调 函 数 同 样 会 处 理 第 二 个 用 于 后 台 操 作 Handler 的 消 息 如 果 要 更 新 ProgressBar, 只 需 如 下 所 示 发 送 一 个 简 单 的 消 息 : Message.obtain(mMainHandler, SET_PROGRESS, progress, maxvalue). sendtotarget(); 任 何 必 须 在 主 线 程 运 行 的 操 作 都 可 以 使 用 这 种 方 法 既 可 以 像 上 面 一 样 发 送 简 单 的 Message 对 象, 也 可 以 给 obj 成 员 变 量 设 置 更 复 杂 的 数 据, 或 者 在 setdata 中 设 置 Bundle 参 数 只 需 要 确 保 把 消 息 发 送 给 正 确 的 Handler 即 可 选 择 合 适 的 线 程 前 面 显 示 了 三 种 在 Android 上 创 建 和 使 用 线 程 的 方 式 API 中 和 线 程 相 关 的 类 还 有 ExecutorService 和 Loader ExecutorService 适 合 处 理 并 行 运 行 的 多 个 任 务, 这 非 常 适 合 编 写 响 应 多 客 户 端 的 服 务 器 应 用 AsyncTask 内 部 同 样 使 用 ExecutorService 处 理 多 线 程 如 果 希 望 能 够 并 行 执 行 多 个 AsyncTask, 也 可 以 通 过 使 用 正 确 的 ExecutorService 来 完 成 码 农 45

如 果 需 要 一 个 专 门 的 线 程 来 进 行 操 作, 可 以 从 前 面 所 示 的 三 个 例 子 开 始 不 建 议 直 接 使 用 Thread 类, 除 非 是 要 完 全 控 制 线 程 的 执 行 大 多 数 情 况 下 推 荐 使 用 AsyncTask 和 Handler 类, 具 体 使 用 哪 一 个 取 决 于 具 体 的 需 求 如 果 不 是 很 频 繁 地 执 行 操 作, 比 如 超 过 每 分 钟 一 次, 那 么 AsyncTask 可 能 是 个 不 错 的 选 择 如 果 需 要 安 排 操 作 的 时 间 或 者 需 要 快 速 间 隔 地 执 行 操 作,Handler 会 是 更 好 的 选 择 从 长 远 来 看, 使 用 Handler 生 成 的 代 码 更 少, 不 过 AsyncTask 更 容 易 使 用 小 结 Android 编 程 实 战 从 自 定 义 视 图 多 点 触 摸 手 势 讲 起, 到 集 成 在 线 Web 服 务, 进 一 步 扩 展 到 探 索 地 理 围 栏 和 活 动 识 别 等 新 技 术 作 为 一 名 经 验 丰 富 的 Android 工 程 师,Erik Hellman 通 过 本 书 深 入 剖 析 了 一 些 专 业 技 巧 诀 窍 容 易 绊 倒 开 发 者 的 陷 阱, 以 及 一 些 鲜 为 人 知 的 技 术 本 文 节 选 自 Android 编 程 实 战 本 文 介 绍 了 Java SE 5.0 的 几 个 高 级 的 特 性, 有 了 JIT Dalvik 虚 拟 机, 开 发 者 就 可 以 在 Android 上 安 全 地 使 用 它 们 了 了 解 和 使 用 这 些 特 性 可 以 简 化 代 码 编 写, 从 而 让 代 码 更 易 于 测 试 和 维 护 本 文 同 样 介 绍 了 怎 样 使 用 java. util.concurrent 包 中 的 并 发 API, 而 不 需 要 自 己 实 现 队 列 和 锁 重 复 造 轮 子 是 一 个 很 常 见 但 是 很 大 的 错 误 : 你 需 要 测 试 和 维 护 更 多 的 代 码, 而 代 码 多 了 也 更 容 易 引 入 bug 本 文 同 样 解 释 了 怎 样 避 免 内 存 分 配 的 诸 多 陷 阱 如 果 在 代 码 中 创 建 了 很 多 临 时 的 生 命 周 期 短 的 变 量, 应 用 程 序 很 可 能 在 用 户 界 面 上 表 现 不 佳 高 效 且 安 全 地 重 用 对 象 可 以 带 来 更 流 畅 的 用 户 体 验 本 文 最 后 介 绍 了 三 种 在 Android 上 使 用 线 程 的 方 法, 但 只 推 荐 使 用 其 中 的 两 个 (AsyncTask 和 Handler) 多 线 程 是 一 个 复 杂 的 话 题, 往 往 是 很 多 难 以 发 现 的 bug 的 原 因 始 终 尝 试 使 用 现 有 的 工 具 类 来 处 理 线 程, 因 为 它 们 会 让 事 情 变 得 更 简 单, 且 允 许 开 发 者 关 注 自 己 代 码 的 功 能 Java 是 一 门 强 大 的 语 言, 它 使 开 发 人 员 更 容 易 表 达 他 们 想 要 实 现 的 目 标 学 会 如 何 更 有 效 地 使 用 它 会 让 你 成 为 更 优 秀 的 开 发 者, 并 帮 助 开 发 者 创 建 高 质 量 的 代 码 码 农 46

专 题 : 进 击 的 Java Java 8 函 数 式 编 程 : 第 一 个 Lambda 表 达 式 Java 8 的 最 大 变 化 是 引 入 了 Lambda 表 达 式 一 种 紧 凑 的 传 递 行 为 的 方 式 接 下 来 我 们 就 了 解 一 下 什 么 是 Lambda 表 达 式 第 一 个 Lambda 表 达 式 作 者 / Richard Warburton Richard 获 得 了 Warwick 大 学 计 算 机 科 学 博 士 学 位, 他 的 研 究 集 中 在 编 译 原 理 Richard 的 职 业 生 涯 一 直 专 注 于 高 性 能 计 算 的 数 据 分 析 他 是 伦 敦 Java 社 区 的 领 导 者 之 一, 也 是 JCP 委 员 会 的 一 员, 针 对 Lambdas 组 织 Adopta-JSR 项 目 他 也 是 知 名 的 演 讲 家, 在 JavaOne DevoxxUK 和 JAX 伦 敦 会 议 上 都 演 说 过 Swing 是 一 个 与 平 台 无 关 的 Java 类 库, 用 来 编 写 图 形 用 户 界 面 (GUI) 该 类 库 有 一 个 常 见 用 法 : 为 了 响 应 用 户 操 作, 需 要 注 册 一 个 事 件 监 听 器 一 旦 用 户 输 入, 监 听 器 会 执 行 一 些 操 作 ( 见 例 1) 例 1 使 用 匿 名 内 部 类 将 行 为 和 按 钮 单 击 进 行 关 联 button.addactionlistener(new ActionListener() { public void actionperformed(actionevent event) { System.out.println("button clicked"); ); 在 这 个 例 子 中, 我 们 创 建 了 一 个 新 对 象, 该 对 象 实 现 了 ActionListener 接 口 这 个 接 口 只 有 一 个 方 法 :actionperformed, 当 用 户 点 击 屏 幕 上 的 按 钮 时, 该 方 法 就 会 被 button 调 用 匿 名 内 部 类 实 现 了 该 方 法 在 例 2-1 中 该 方 法 所 执 行 的 只 是 打 印 出 一 条 信 息, 表 明 按 钮 已 被 点 击 码 农 47