内容提要 |
目录 |
CIP |
序言 |
前言 |
作译者 |
书摘 |
书评 |
最佳拍档 |
从本书获得的各项大奖以及来自世界各地的读者评论中,不难看出这是一本经典之作。本书的作者拥有多年教学经验,对C、C++以及Java语言都有独到、深入的见解,以通俗易懂及小而直接的示例解释了一个个晦涩抽象的概念。本书共22章,包括操作符、控制执行流程、访问权限控制、复用类、多态、接口、通过异常处理错误、字符串、泛型、数组、容器深入研究、Java I/O系统、枚举类型、并发以及图形化用户界面等内容。这些丰富的内容,包含了Java语言基础语法以及高级特性,适合各个层次的Java程序员阅读,同时也是高等院校讲授面向对象程序设计语言以及Java语言的绝佳教材和参考书。
Simplified Chinese edition copyright ?2007 by Pearson Education Asia Limited and China Machine Press.
Original English language title: Thinking in Java, Fourth Edition (ISBN 0-13-187248-6) by Bruce Eckel, Copyright ?2006.
All rights reserved.
Published by arrangement with the original publisher, Pearson Education, Inc., publishing as Prentice Hall PTR.
本书封面贴有Pearson Education(培生教育出版集团)激光防伪标签,无标签者不得销售。
版权所有,侵权必究。
本书法律顾问 北京市展达律师事务所 |
|
目录
出版者的话
专家指导委员会
读者评论
关于《Thinking in C++》
译者序
译者简介
前言
绪论
第1章 对象导论 1
1.1 抽象过程 1
1.2 每个对象都有一个接口 2
1.3 每个对象都提供服务 4
1.4 被隐藏的具体实现 4
1.5 复用具体实现 5
1.6 继承 6
1.6.1 “是一个”与“像是一个”关系 8
1.7 伴随多态的可互换对象 8
1.8 单根继承结构 11
1.9 容器 11
1.9.1 参数化类型 12
1.10 对象的创建和生命期 13
1.11 异常处理:处理错误 14
1.12 并发编程 14
1.13 Java与Internet 15
1.13.1 Web是什么 15
1.13.2 客户端编程 16
1.13.3 服务器端编程 19
1.14 总结 20
第2章 一切都是对象 21
2.1 用引用操纵对象 21
2.2 必须由你创建所有对象 22
2.2.1 存储到什么地方 22
2.2.2 特例:基本类型 23
2.2.3 Java中的数组 24
2.3 永远不需要销毁对象 24
2.3.1 作用域 24
2.3.2 对象的作用域 25
2.4 创建新的数据类型:类 25
2.4.1 字段和方法 26
2.5 方法、参数和返回值 27
2.5.1 参数列表 27
2.6 构建一个Java程序 28
2.6.1 名字可见性 28
2.6.2 运用其他构件 28
2.6.3 static 关键字 29
2.7 你的第一个Java程序 30
2.7.1 编译和运行 31
2.8 注释和嵌入式文档 32
2.8.1 注释文档 32
2.8.2 语法 33
2.8.3 嵌入式HTML 33
2.8.4 一些标签示例 34
2.8.5 文档示例 35
2.9 编码风格 36
2.10 总结 36
2.11 练习 37
第3章 操作符 38
3.1 更简单的打印语句 38
3.2 使用Java操作符 39
3.3 优先级 39
3.4 赋值 39
3.4.1 方法调用中的别名问题 40
3.5 算术操作符 41
3.5.1 一元加、减操作符 43
3.6 自动递增和递减 43
3.7 关系操作符 44
3.7.1 测试对象的等价性 44
3.8 逻辑操作符 45
3.8.1 短路 46
3.9 直接常量 47
3.9.1 指数记数法 48
3.10 按位操作符 49
3.11 移位操作符 49
3.12 三元操作符 if-else 52
3.13 字符串操作符 + 和 += 53
3.14 使用操作符时常犯的错误 54
3.15 类型转换操作符 54
3.15.1 截尾和舍入 55
3.15.2 提升 56
3.16 Java没有sizeof 56
3.17 操作符小结 56
3.18 总结 63
第4章 控制执行流程 64
4.1 true和false 64
4.2 if-else 64
4.3 迭代 65
4.3.1 do-while 65
4.3.2 for 66
4.3.3 逗号操作符 67
4.4 Foreach语法 67
4.5 return 69
4.6 break和 continue 69
4.7 臭名昭著的goto 70
4.8 switch 73
4.9 总结 75
第5章 初始化与清理 76
5.1 用构造器确保初始化 76
5.2 方法重载 77
5.2.1 区分重载方法 79
5.2.2 涉及基本类型的重载 79
5.2.3 以返回值区分重载方法 82
5.3 默认构造器 83
5.4 this关键字 84
5.4.1 在构造器中调用构造器 85
5.4.2 static的含义 86
5.5 清理:终结处理和垃圾回收 87
5.5.1 finalize()的用途何在 87
5.5.2 你必须实施清理 88
5.5.3 终结条件 88
5.5.4 垃圾回收器如何工作 89
5.6 成员初始化 91
5.6.1 指定初始化 93
5.7 构造器初始化 94
5.7.1 初始化顺序 94
5.7.2 静态数据的初始化 95
5.7.3 显式的静态初始化 96
5.7.4 非静态实例初始化 97
5.8 数组初始化 98
5.8.1 可变参数列表 102
5.9 枚举类型 105
5.10 总结 107
第6章 访问权限控制 109
6.1 包:库单元 110
6.1.1 代码组织 110
6.1.2 创建独一无二的包名 111
6.1.3 定制工具库 114
6.1.4 用 import改变行为 115
6.1.5 对使用包的忠告 115
6.2 Java访问权限修饰词 116
6.2.1 包访问权限 116
6.2.2 public:接口访问权限 116
6.2.3 private:你无法访问 118
6.2.4 protected:继承访问权限 118
6.3 接口和实现 120
6.4 类的访问权限 121
6.5 总结 123
第7章 复用类 125
7.1 组合语法 125
7.2 继承语法 127
7.2.1 初始化基类 129
7.3 代理 130
7.4 结合使用组合和继承 132
7.4.1 确保正确清理 133
7.4.2 名称屏蔽 135
7.5 在组合与继承之间选择 137
7.6 protected关键字 138
7.7 向上转型 139
7.7.1 为什么称为向上转型 139
7.7.2 再论组合与继承 140
7.8 final关键字 140
7.8.1 final 数据 140
7.8.2 final 方法 143
7.8.3 final 类 144
7.8.4 有关final的忠告 145
7.9 初始化及类的加载 145
7.9.1 继承与初始化 146
7.10 总结 147
第8章 多态 148
8.1 再论向上转型 148
8.1.1 忘记对象类型 149
8.2 转机 150
8.2.1 方法调用绑定 150
8.2.2 产生正确的行为 151
8.2.3 可扩展性 153
8.2.4 缺陷:“覆盖”私有方法 156
8.2.5 缺陷:域与静态方法 156
8.3 构造器和多态 157
8.3.1 构造器的调用顺序 157
8.3.2 继承与清理 159
8.3.3 构造器内部的多态方法的行为 162
8.4 协变返回类型 164
8.5 用继承进行设计 165
8.5.1 纯继承与扩展 166
8.5.2 向下转型与运行时类型识别 167
8.6 总结 168
第9章 接口 169
9.1 抽象类和抽象方法 169
9.2 接口 172
9.3 完全解耦 174
9.4 Java中的多重继承 178
9.5 通过继承来扩展接口 180
9.5.1 组合接口时的名字冲突 181
9.6 适配接口 181
9.7 接口中的域 183
9.7.1 初始化接口中的域 184
9.8 嵌套接口 185
9.9 接口与工厂 186
9.10 总结 188
第10章 内部类 190
10.1 创建内部类 190
10.2 链接到外部类 191
10.3 使用.this与.new 193
10.4 内部类与向上转型 194
10.5 在方法和作用域内的内部类 195
10.6 匿名内部类 196
10.6.1 再访工厂方法 199
10.7 嵌套类 201
10.7.1 接口内部的类 202
10.7.2 从多层嵌套类中访问外部类的
成员 203
10.8 为什么需要内部类 204
10.8.1 闭包与回调 205
10.8.2 内部类与控制框架 207
10.9 内部类的继承 212
10.10 内部类可以被覆盖吗 212
10.11 局部内部类 214
10.12 内部类标识符 215
10.13 总结 215
第11章 持有对象 216
11.1 泛型和类型安全的容器 216
11.2 基本概念 219
11.3 添加一组元素 220
11.4 容器的打印 221
11.5 List 223
11.6 迭代器 226
11.6.1 ListIterator 227
11.7 LinkedList 228
11.8 Stack 229
11.9 Set 231
11.10 Map 233
11.11 Queue 236
11.11.1 PriorityQueue 237
11.12 Collection和Iterator 238
11.13 Foreach与迭代器 241
11.13.1 适配器方法惯用法 243
11.14 总结 245
第12章 通过异常处理错误 248
12.1 概念 248
12.2 基本异常 249
12.2.1 异常参数 250
12.3 捕获异常 250
12.3.1 try块 250
12.3.2 异常处理程序 250
12.4 创建自定义异常 251
12.4.1 异常与记录日志 253
12.5 异常说明 256
12.6 捕获所有异常 256
12.6.1 栈轨迹 257
12.6.2 重新抛出异常 258
12.6.3 异常链 260
12.7 Java标准异常 263
12.7.1 特例:RuntimeException 263
12.8 使用finally进行清理 264
12.8.1 finally用来做什么 265
12.8.2 在return中使用finally 267
12.8.3 缺憾:异常丢失 268
12.9 异常的限制 269
12.10 构造器 271
12.11 异常匹配 275
12.12 其他可选方式 276
12.12.1 历史 277
12.12.2 观点 278
12.12.3 把异常传递给控制台 279
12.12.4 把“被检查的异常”转换为
“不检查的异常” 279
12.13 异常使用指南 281
12.14 总结 281
第13章 字符串 283
13.1 不可变String 283
13.2 重载“+”与StringBuilder 283
13.3 无意识的递归 287
13.4 String上的操作 288
13.5 格式化输出 289
13.5.1 printf() 289
13.5.2 System.out.format() 289
13.5.3 Formatter类 290
13.5.4 格式化说明符 291
13.5.5 Formatter转换 292
13.5.6 String.format() 294
13.6 正则表达式 295
13.6.1 基础 295
13.6.2 创建正则表达式 297
13.6.3 量词 299
13.6.4 Pattern和Matcher 300
13.6.5 split() 305
13.6.6 替换操作 306
13.6.7 reset() 307
13.6.8 正则表达式与Java I/O 307
13.7 扫描输入 309
13.7.1 Scanner定界符 310
13.7.2 用正则表达式扫描 311
13.8 StringTokenizer 312
13.9 总结 312
第14章 类型信息 313
14.1 为什么需要RTTI 313
14.2 Class对象 314
14.2.1 类字面常量 318
14.2.2 泛化的Class引用 320
14.2.3 新的转型语法 322
14.3 类型转换前先做检查 322
14.3.1 使用类字面常量 327
14.3.2 动态的instanceof 329
14.3.3 递归计数 330
14.4 注册工厂 331
14.5 instanceof 与 Class的等价性 333
14.6 反射:运行时的类信息 334
14.6.1 类方法提取器 335
14.7 动态代理 337
14.8 空对象 341
14.8.1 模拟对象与桩 346
14.9 接口与类型信息 346
14.10 总结 350
第15章 泛型 352
15.1 与C++的比较 352
15.2 简单泛型 353
15.2.1 一个元组类库 354
15.2.2 一个堆栈类 356
15.2.3 RandomList 357
15.3 泛型接口 358
15.4 泛型方法 361
15.4.1 杠杆利用类型参数推断 362
15.4.2 可变参数与泛型方法 363
15.4.3 用于Generator的泛型方法 364
15.4.4 一个通用的Generator 364
15.4.5 简化元组的使用 366
15.4.6 一个Set实用工具 367
15.5 匿名内部类 369
15.6 构建复杂模型 371
15.7 擦除的神秘之处 372
15.7.1 C++的方式 373
15.7.2 迁移兼容性 375
15.7.3 擦除的问题 376
15.7.4 边界处的动作 377
15.8 擦除的补偿 380
15.8.1 创建类型实例 381
15.8.2 泛型数组 383
15.9 边界 386
15.10 通配符 389
15.10.1 编译器有多聪明 391
15.10.2 逆变 393
15.10.3 无界通配符 395
15.10.4 捕获转换 399
15.11 问题 400
15.11.1 任何基本类型都不能作为类型
参数 400
15.11.2 实现参数化接口 401
15.11.3 转型和警告 402
15.11.4 重载 403
15.11.5 基类劫持了接口 404
15.12 自限定的类型 404
15.12.1 古怪的循环泛型 404
15.12.2 自限定 405
15.12.3 参数协变 407
15.13 动态类型安全 409
15.14 异常 410
15.15 混型 412
15.15.1 C++中的混型 412
15.15.2 与接口混合 413
15.15.3 使用装饰器模式 414
15.15.4 与动态代理混合 415
15.16 潜在类型机制 416
15.17 对缺乏潜在类型机制的补偿 420
15.17.1 反射 420
15.17.2 将一个方法应用于序列 421
15.17.3 当你并未碰巧拥有正确的
接口时 423
15.17.4 用适配器仿真潜在类型机制 424
15.18 将函数对象用作策略 426
15.19 总结:转型真的如此之糟吗? 430
15.19.1 进阶读物 432
第16章 数组 433
16.1 数组为什么特殊 433
16.2 数组是第一级对象 434
16.3 返回一个数组 436
16.4 多维数组 437
16.5 数组与泛型 440
16.6 创建测试数据 442
16.6.1 Arrays.fill() 442
16.6.2 数据生成器 443
16.6.3 从Generator中创建数组 447
16.7 Arrays实用功能 450
16.7.1 复制数组 450
16.7.2 数组的比较 451
16.7.3 数组元素的比较 452
16.7.4 数组排序 454
16.7.5 在已排序的数组中查找 455
16.8 总结 457
第17章 容器深入研究 459
17.1 完整的容器分类法 459
17.2 填充容器 460
17.2.1 一种Generator解决方案 460
17.2.2 Map生成器 462
17.2.3 使用Abstract类 464
17.3 Collection的功能方法 470
17.4 可选操作 472
17.4.1 未获支持的操作 473
17.5 List的功能方法 474
17.6 Set和存储顺序 477
17.6.1 SortedSet 479
17.7 队列 480
17.7.1 优先级队列 481
17.7.2 双向队列 482
17.8 理解Map 483
17.8.1 性能 484
17.8.2 SortedMap 486
17.8.3 LinkedHashMap 487
17.9 散列与散列码 488
17.9.1 理解hashCode() 490
17.9.2 为速度而散列 492
17.9.3 覆盖hashCode() 495
17.10 选择接口的不同实现 499
17.10.1 性能测试框架 499
17.10.2 对List的选择 502
17.10.3 微基准测试的危险 507
17.10.4 对Set的选择 508
17.10.5 对Map的选择 509
17.11 实用方法 512
17.11.1 List的排序和查询 514
17.11.2 设定Collection或Map为不可
修改 515
17.11.3 Collection或Map的同步控制 516
17.12 持有引用 518
17.12.1 WeakHashMap 519
17.13 Java 1.0/1.1 的容器 520
17.13.1 Vector 和 Enumeration 520
17.13.2 Hashtable 521
17.13.3 Stack 521
17.13.4 BitSet 522
17.14 总结 524
第18章 Java I/O系统 525
18.1 File类 525
18.1.1 目录列表器 525
18.1.2 目录实用工具 528
18.1.3 目录的检查及创建 532
18.2 输入和输出 533
18.2.1 InputStream类型 534
18.2.2 OutputStream类型 535
18.3 添加属性和有用的接口 535
18.3.1 通过FilterInputStream从
InputStream读取数据 535
18.3.2 通过FilterOutPutStream向
OutputStream写入 536
18.4 Reader和Writer 537
18.4.1 数据的来源和去处 537
18.4.2 更改流的行为 538
18.4.3 未发生变化的类 539
18.5 自我独立的类:RandomAccessFile 539
18.6 I/O流的典型使用方式 539
18.6.1 缓冲输入文件 540
18.6.2 从内存输入 540
18.6.3 格式化的内存输入 541
18.6.4 基本的文件输出 542
18.6.5 存储和恢复数据 543
18.6.6 读写随机访问文件 544
18.6.7 管道流 545
18.7 文件读写的实用工具 545
18.7.1 读取二进制文件 548
18.8 标准I/O 548
18.8.1 从标准输入中读取 548
18.8.2 将System.out转换成PrintWriter 549
18.8.3 标准I/O重定向 549
18.9 进程控制 550
18.10 新I/O 551
18.10.1 转换数据 554
18.10.2 获取基本类型 556
18.10.3 视图缓冲器 557
18.10.4 用缓冲器操纵数据 560
18.10.5 缓冲器的细节 560
18.10.6 内存映射文件 563
18.10.7 文件加锁 566
18.11 压缩 568
18.11.1 用GZIP进行简单压缩 568
18.11.2 用Zip进行多文件保存 569
18.11.3 Java档案文件 570
18.12 对象序列化 571
18.12.1 寻找类 574
18.12.2 序列化的控制 575
18.12.3 使用“持久性” 581
18.13 XML 586
18.14 Preferences 588
18.15 总结 589
第19章 枚举类型 590
19.1 基本enum特性 590
19.1.1 将静态导入用于enum 591
19.2 向enum中添加新方法 591
19.2.1 覆盖enum的方法 592
19.3 switch语句中的enum 593
19.4 values()的神秘之处 594
19.5 实现,而非继承 596
19.6 随机选取 596
19.7 使用接口组织枚举 597
19.8 使用EnumSet替代标志 600
19.9 使用EnumMap 602
19.10 常量相关的方法 603
19.10.1 使用enum的职责链 606
19.10.2 使用enum的状态机 609
19.11 多路分发 613
19.11.1 使用enum分发 615
19.11.2 使用常量相关的方法 616
19.11.3 使用EnumMap分发 618
19.11.4 使用二维数组 618
19.12 总结 619
第20章 注解 620
20.1 基本语法 620
20.1.1 定义注解 621
20.1.2 元注解 622
20.2 编写注解处理器 622
20.2.1 注解元素 623
20.2.2 默认值限制 624
20.2.3 生成外部文件 624
20.2.4 注解不支持继承 627
20.2.5 实现处理器 627
20.3 使用apt处理注解 629
20.4 将观察者模式用于apt 632
20.5 基于注解的单元测试 634
20.5.1 将@Unit用于泛型 641
20.5.2 不需要任何“套件” 642
20.5.3 实现@Unit 642
20.5.4 移除测试代码 647
20.6 总结 649
第21章 并发 650
21.1 并发的多面性 651
21.1.1 更快的执行 651
21.1.2 改进代码设计 653
21.2 基本的线程机制 653
21.2.1 定义任务 654
21.2.2 Thread类 655
21.2.3 使用Executor 656
21.2.4 从任务中产生返回值 658
21.2.5 休眠 659
21.2.6 优先级 660
21.2.7 让步 661
21.2.8 后台线程 662
21.2.9 编码的变体 665
21.2.10 术语 669
21.2.11 加入一个线程 669
21.2.12 创建有响应的用户界面 671
21.2.13 线程组 672
21.2.14 捕获异常 672
21.3 共享受限资源 674
21.3.1 不正确地访问资源 674
21.3.2 解决共享资源竞争 676
21.3.3 原子性与易变性 680
21.3.4 原子类 684
21.3.5 临界区 685
21.3.6 在其他对象上同步 689
21.3.7 线程本地存储 690
21.4 终结任务 691
21.4.1 装饰性花园 691
21.4.2 在阻塞时终结 694
21.4.3 中断 695
21.4.4 检查中断 701
21.5 线程之间的协作 702
21.5.1 wait()与notifyAll() 703
21.5.2 notify()与notifyAll() 707
21.5.3 生产者与消费者 709
21.5.4 生产者-消费者与队列 713
21.5.5 任务间使用管道进行输入/输出 717
21.6 死锁 718
21.7 新类库中的构件 722
21.7.1 CountDownLatch 722
21.7.2 CyclicBarrier 724
21.7.3 DelayQueue 726
21.7.4 PriorityBlockingQueue 728
21.7.5 使用ScheduledExecutor的温室
控制器 730
21.7.6 Semaphore 733
21.7.7 Exchanger 735
21.8 仿真 737
21.8.1 银行出纳员仿真 737
21.8.2 饭店仿真 741
21.8.3 分发工作 744
21.9 性能调优 748
21.9.1 比较各类互斥技术 748
21.9.2 免锁容器 754
21.9.3 乐观加锁 760
21.9.4 ReadWriteLock 761
21.10 活动对象 763
21.11 总结 766
21.11.1 进阶读物 767
第22章 图形化用户界面 768
22.1 applet 769
22.2 Swing基础 769
22.2.1 一个显示框架 771
22.3 创建按钮 772
22.4 捕获事件 773
22.5 文本区域 774
22.6 控制布局 776
22.6.1 BorderLayout 776
22.6.2 FlowLayout 776
22.6.3 GridLayout 777
22.6.4 GridBagLayout 777
22.6.5 绝对定位 778
22.6.6 BoxLayout 778
22.6.7 最好的方式是什么 778
22.7 Swing事件模型 778
22.7.1 事件与监听器的类型 779
22.7.2 跟踪多个事件 783
22.8 Swing组件一览 785
22.8.1 按钮 785
22.8.2 图标 787
22.8.3 工具提示 788
22.8.4 文本域 789
22.8.5 边框 790
22.8.6 一个迷你编辑器 791
22.8.7 复选框 792
22.8.8 单选按钮 793
22.8.9 组合框 793
22.8.10 列表框 794
22.8.11 页签面板 796
22.8.12 消息框 796
22.8.13 菜单 798
22.8.14 弹出式菜单 802
22.8.15 绘图 803
22.8.16 对话框 805
22.8.17 文件对话框 808
22.8.18 Swing组件上的HTML 809
22.8.19 滑块与进度条 810
22.8.20 选择外观 811
22.8.21 树、表格和剪贴板 812
22.9 JNLP与Java Web Start 812
22.10 Swing与并发 816
22.10.1 长期运行的任务 816
22.10.2 可视化线程机制 822
22.11 可视化编程与JavaBean 823
22.11.1 JavaBean是什么 824
22.11.2 使用Introspector抽取出
BeanInfo 825
22.11.3 一个更复杂的Bean 829
22.11.4 JavaBean与同步 831
22.11.5 把Bean打包 834
22.11.6 对Bean更高级的支持 835
22.11.7 有关Bean的其他读物 836
22.12 Swing的可替代选择 836
22.13 用Flex构建Flash Web客户端 836
22.13.1 Hello, Flex 837
22.13.2 编译MXML 838
22.13.3 MXML与ActionScript 838
22.13.4 容器与控制 839
22.13.5 效果与样式 840
22.13.6 事件 841
22.13.7 连接到Java 841
22.13.8 数据模型与数据绑定 843
22.13.9 构建和部署 843
22.14 创建SWT应用 844
22.14.1 安装SWT 845
22.14.2 Hello, SWT 845
22.14.3 根除冗余代码 847
22.14.4 菜单 848
22.14.5 页签面板、按钮和事件 849
22.14.6 图形 852
22.14.7 SWT中的并发 853
22.14.8 SWT还是Swing 855
22.15 总结 855
22.15.1 资源 855
附录A 补充材料 856
附录B 资源 859
索引 863 |
 |
|
图书在版编目(CIP)数据
Java编程思想(第4版) /(美)埃克尔(Eckel, B.)著;陈昊鹏译. -北京:机械工业出版社,2007.6
(计算机科学丛书)
书名原文:Thinking in Java, Fourth Edition
ISBN 978-7-111-21382-6
Ⅰ. J… Ⅱ. ① 埃… ② 陈… Ⅲ. Java语言-程序设计 Ⅳ. TP312
中国版本图书馆CIP数据核字(2007)第060047号 |
|
|
绪 论
“上帝赋予人类说话的能力,而言语又创造了思想,思想是人类对宇宙的量度。”
—摘自《Prometheus Unbound》,Shelley
人类……极其受那些已经成为社会表达工具的特定语言的支配。想像一下,如果一个人可以不使用语言就能够从本质上适应现实世界,语言仅仅是解决具体的交流和反映问题时偶尔才用到的方式,我们会发现,这只能是一种幻想。事实上,“真实世界”在很大程度上是不知不觉地基于群体的语言习惯形成的。
—摘自《The Status of Linguistics As A Science》,1929, Edward Sapir
如同任何人类语言一样,Java提供了一种表达概念的方式。如果使用得当,随着问题变得更庞大更复杂,这种表达工具将会比别的可供选择的语言更为简单、灵活。
我们不应该将Java仅仅看作是一些特性的集合—有一些特性在孤立状态下没有任何意义。只有在考虑到设计,而不仅仅是编码时,才能完整地运用Java的各部分。而且,要按照这种方式来理解Java,必须理解在语言和编程中经常碰到的问题。这本书讨论的是编程问题,它们为什么成为问题,以及Java已经采取什么样的方案来解决它们。因此,每章所阐述的特性集,都是基于我所看到的这一语言在解决特定类型问题时的方式。按照这种方式,我希望能够每次引导读者前进一点,直到Java思想意识成为你最自然不过的语言。
自始至终,我一直持这样的观点:你需要在头脑中创建一个模型,以加强对这种语言的深入理解;如果你遇到了疑问,就将它反馈到头脑中的模型并推断出答案。
前提条件
本书假定你对程序设计有一定程度的熟悉:你已经知道程序是一些语句的集合,知道子程序/函数/宏的概念,知道像“if”这样的控制语句和像“while”这样的循环结构,等等。不过,你可能在许多地方已经学到过这些,例如使用宏语言进行程序设计,或者使用像Perl这样的工具工作。只要你已经达到能够自如地运用程序设计基本思想的程度,你就能够顺利阅读本书。当然,本书对C程序员来说更容易,对于C++程序员更是如此,但是,即使你没有实践过这两种语言,也不要否定自己—而应该更加努力学习。并且,从www.MindView.net处可下载的《Thinking in C》多媒体研讨课能够带领你快速学习所必需的Java基础知识)。不过,我还会介绍面向对象(OOP)的概念和Java的基本控制机制。
尽管本书可能会经常引用、参考C和C++语言的特性,但这并不是打算让它们成为内部注释,而是要帮助所有的程序员正确看待这些语言,毕竟Java是从这些语言衍生而来的。我会努力简化这些引用、参考,并且对那些我认为一个非C/C++程序员可能不太熟悉的地方加以解释。
学习Java
大概在我的第一本书《Using C++》(Osborne/McGraw-Hill,1989)出版发行的同一时候,我就开始教授这种语言了。讲授程序设计语言已经成为我的职业了;自1987年以来,我在世界各地的听众中看到,有的昏昏欲睡,有的面无表情,有的表情迷茫。当我开始给一些小团体进行室内培训时,在这些实践中我发现了一些事情。即使那些面带微笑频频点头的人也对很多问题心存困惑。我发现,多年来在软件开发会议上由我主持的C++分组讨论会(后来变成Java分组讨论会)中,我和其他的演讲者往往是在极短的时间内告诉听众许多话题。因此,最后由于听众的水平不同和讲授教材的方式这两方面的原因,我可能最终会失去一部分听众。可能这样要求得太多了,但因为我是传统演讲的反对者之一(而且对于大多数人来说,我相信这种抵制是因为厌倦),因此我想尽力让每个人都可以跟得上演讲的进度。
我曾经一度在相当短的时间内做了一系列不同的演讲。因此,我结束了“实践和迭代”(一项在Java程序设计中也得到很好运用的技术)的学习。最后,我根据自己在教学实践中学到的东西发展出一门课程。我的公司—MindView有限公司现在提供公开的室内“Thinking in Java”研讨课;这是我们主要的初级研讨课,为以后更高级的研讨课提供基础。读者可以到网站www.MindView.net上了解详细情况。(初级研讨课在“Hands-On Java”光盘上也能找到。上述网站也可以找到相关信息。)
从每个研讨课获得的反馈信息都可以帮助我去修改和重新制定课程教材,直到我认为它能够成为一个良性运转的教学工具为止。不过不能将本书视为一般的研讨课笔记;我努力在本书中放入尽可能多的信息,并且合理地组织本书结构,从而引导读者顺利进入下一主题。最重要的是,本书面向那些孤军奋战一门新的程序设计语言的读者。
目标
就像我前一本书《Thinking in C++》那样,在设计本书时,我脑子里始终思考的一件事情就是:人们学习语言的方式。当我思索书中的一章时,我思索的是如何在研讨课上教好一堂课。研讨课听众的反馈意见帮助我理解了哪些是需要详细阐明的有难度的部分。在某些领域,我一开始雄心勃勃,在其中一下子囊括了过多的特性,后来通过在讲解这些材料的过程中,我逐渐意识到如果要囊括过多的特性,就必须对它们全部解释清楚,而这很容易使学生产生混淆。
因此,本书的每一章都设法只教授一个特性,或者一小组互相关联的特性,并且不会依赖于还未介绍的概念。通过这种方式,你可以在你当前所掌握的知识背景下,在继续向前学习之前,消化吸收每一部分内容。
在这本书中我想达到的目标是:
1) 每一次只演示一个步骤的材料,以便读者在继续后面的学习之前可以很容易地消化吸收每一个观念。仔细地对特性的讲解进行排序,以使得你在看到对某个特性的运用之前,会先了解它。当然,这并非总是可行的,在那些不可行的情况下,会给出一个简短的介绍性描述。
2) 使用的示例尽可能简单、短小。这样做有时会妨碍我们解决“真实世界”的问题,但是,我发现对于初学者,能够理解例子的每一个细节,而不是理解它所能够解决的问题范畴,前者通常更能为他们带来愉悦。同样,适合在教室内学习的代码数量也有严格限制。正因为如此,我将毫无疑问地会遭到批评-批评我使用“玩具般的示例”,但是我乐意接受那些有利于为教育带来益处的种种事物。
3) 向读者提供“我认为对理解这种程序设计语言来说很重要”的部分,而不是提供我所知道的所有事情。我相信信息在重要性上存在层次差别,有一些事实对于95%的程序员来说永远不必知道—那些只会困扰他们并且使他们对程序复杂性平添许多感触。举一个C语言的例子,如果能够记住操作符优先表(我从未记住过),那么可以写出灵巧的代码。但是你要再想一想,这样做会给读者/维护者带来困惑。因此忘掉优先权,在不是很清楚的时候使用圆括号就行了。
4) 使每部分的重点足够明确,以便缩短教学和练习之间的时间。这样做不仅使听众在亲身参与研讨课时思维更为活跃和集中,而且还可以让读者更具有成就感。
5) 给读者打下坚实的基础,使读者能够充分理解问题,以便转入更难的课程学习和书籍阅读中。
根据本书教学
本书最初的版本是从一个为期一周的研讨课演变而来的,当时Java还处于初级阶段,因此一周已经足以覆盖Java的语言特性了。随着Java的成长,有越来越多的特性和类库不断地添加了进来,我固执地试图仍旧在一周内教授所有的内容。那时,有一位顾客请我讲课,内容“只包括基础知识”,我教授的过程中,我发现在一周的时间内填鸭式的教授所有的内容,对于我自己和参加研讨课的人来说,都是一种痛苦。Java已经不再是一种可以在一周内教授的“简单”语言了。
这份精力和感悟在极大程度上促使我对本书进行了重新的组织,现在它已经被设计为可以支撑一个两周的研讨课,或者是一门两学期的大学课程。介绍性的部分在“通过异常处理错误”一章就结束了,但是你可能还想补充了解一些对JDBC、Servlet和JSP的介绍,这些内容构成了另外一门基础课程,即Hands-on Java 光盘的核心内容。本书剩余部分可以组成一门中级课程,即Intermediate Thinking in Java光盘中所包含的材料。这两张光盘在www.MindView.net都有售。
通过www.prenhallprofessional.com与Prentice-Hall联系,可以得到能够教授本书这些材料的教师信息。
JDK的HTML文档
Sun公司的Java语言及其类库(可以从java.sun.com免费下载)配套提供了电子版文档,可使用Web浏览器阅读。许多出版的Java书籍中也都有这份文档的备份。你可能已经拥有了它,或者能够下载;所以除非必要,本书不会再重复那份文档。因为一般来说,用Web浏览器查找类的描述比在书中查找要快得多(并且在线文档更能保持更新)。你仅需要参考“JDK文档”。只有当需要对文档进行补充,以便你能够理解特定实例时,本书才会提供有关类的一些附加说明。
练习
在研讨课上,我发现一些简单的练习非常利于学生们理解掌握有关概念,因此在每一章的最后都安排了一些习题。
大多数练习设计得都很简单,可以让学生在课堂上在合理的时间内完成这些作业,以便指导老师检查辅导以确保所有的学生都吸收了教材的内容。有一些题目具有挑战性,但并没有难度很高的题目。
一些经过挑选的练习答案可以在名为The Thinking in Java Annotated Solution Guide的电子文档中找到,仅需少许费用便可以从www.MindView.net下载得到。
Java基础
本书还附送可从www.MindView.net处下载的免费的多媒体研讨课。这是《Thinking in C》的研讨课,它介绍了Java语法沿用的C语言中的语法、操作符及函数。在本书以前的版本中,这部分内容收录在随书附送的“Java基础”CD中,但是现在这个研讨课可以免费下载了。
我原本打算让Chuck Allison把“Thinking in C”创建成一个独立产品,不过我还是决定将它和第2版的《Thinking in C++》,以及第2版和第3版的《Thinking in Java》包含在一起,这样做是为了让参加研讨课的、没有太多C语言基本语法背景的人们能够很方便地找到相关资料。应该抛开这种思想:“我是一个聪明的程序员,我不想学习C,而想学习C++或Java,因此我会跳过C直接到C++/Java。”在到了研讨课上后,这些人渐渐明白,很好地理解C语言语法这个先决条件很必要。
技术在不断发生变化,将《Thinking in C》重新制作为可下载的Flash形式比将其收录在CD中要更具实际意义。通过在线提供这个研讨课,我可以保证每个人都可以事先做好充足的准备。
《Thinking in C》研讨课也让本书获得了更多的读者。尽管本书中“操作符”和“控制执行流程”两章覆盖了Java继承自C的基本部分,但是在线研讨课仍旧是更好的介绍,而且它要求学生所具备的程序设计背景比这本书要求的还要少。
源代码
本书的所有源代码都能以保留版权的免费软件的形式得到,它们是以单一包的形式发布的,访问www.MindView.net网站便可获取。为了确保你获得的是最新版本,这个发布这些源代码和本书电子版的网站是一个官方网站。你可以在课堂或其他教育场所发布这些代码。
保留版权的主要目的是为了确保源代码能够被正确地引用,并且防止在未经许可的情况下,在出版媒体中重新发布这些代码(只要说明是引用了这些代码,那么在大多数媒介中使用本书中的示例通常不是问题)。
在每个源码文件中,都包含下述版权声明文字:
你可以在自己的项目中引用这些代码,也可以在课堂上引用它们(包括你的演示材料),只要保留每个源文件中出现的保留版权声明即可。
编码标准
在本书的正文中,标识符(方法、变量和类名)排为粗体。大多数关键字也排为粗体,但是不包括那些频繁使用的关键字,例如“class”,因为如果将它们也设为粗体会令人十分厌烦。
对于本书中的示例,我使用了一种特定的编码格式,此格式尽可能地遵循了Sun自己在所有代码中实际使用的格式,在它的网站上你会发现这些代码(见java.sun.com/docs/codeconv/ index.html),并且似乎大多数Java开发环境都支持这种格式。如果你已经读过我的其他著作,你会注意到Sun的编码格式与我的一致-尽管这与我没什么关系(我了解这一点),但我还是很高兴。对代码进行格式化这个议题常常会招致几个小时的热烈争论,因此我不会试图通过自己的示例来规定正确的格式;我对自己使用的格式有自己的想法。因为Java是一种自由形式的程序设计语言,所以你可以继续使用自己喜欢的格式。编码风格问题的一种解决方案是使用像Jalopy(www.triemax.com)这样的工具来将格式转变为适合你的形式,该工具帮助我撰写了此书。
本书中打印的代码文件都用一个自动系统进行过测试,应该全部都能够运行,而且无编译错误。
本书聚焦于Java SE5/6,并用它们进行过测试。如果你需要学习本书这一版中没有讨论的Java语言的先前版本,可以从www.MindView.net处免费下载本书的第1版到第3版。
错误
无论作者使用多少技巧去查找错误,但是有些错误还是悄悄地潜藏了起来,并且经常对新读者造成困扰。如果你发现了任何你确信是错误的东西,请使用在www.MindView.net处可以找到的为本书专设的链接来提交错误以及你建议的修正。对你的帮助我将不胜感激 |
|
前 言
一开始,我只是将Java看作“又一种程序设计语言”。从许多方面看,它也的确如此。
但随着时间流逝,以及对Java的深入研究,我渐渐发现,与我所见过的其他编程语言相比,Java有着完全不同的核心目的。
程序设计其实是对复杂性的管理:待解决问题的复杂性,以及用来解决该问题的工具的复杂性。正是这种复杂性,导致多数程序设计项目失败。在我所知的所有程序设计语言中,几乎没有哪个将自己的设计目标专注于克服开发与维护程序的复杂性。当然,有些编程语言在设计决策时也曾考虑到复杂性的问题,然而,总是会有其他议题被认为更有必要加入到该语言中。于是不可避免地,正是这些所谓更必要的议题导致程序员最终“头撞南墙”。例如,C++选择向后兼容C(以便更容易吸引C程序员),以及具备C一样的高效率。这两点都是非常有益的设计目标,也确实促成了C++的成功,然而它们却暴露出更多的复杂性问题,而这也使得很多项目不得善终(你自然可以责怪程序员或者项目管理,但是,如果一种语言能够帮助你解决错误,那何乐而不为呢?)。再看一个例子,Visual Basic(VB)选择与Basic绑在一起,而Basic并未被设计为具备可扩展性的程序设计语言,结果呢,建立在VB之上的所有扩展都导致了无法维护的语法。还有Perl,它向后兼容awk、sed、grep,以及所有它打算替代的Unix工具,结果呢,人们开始指责Perl程序成了“不可阅读(write-only)的代码”(即,只要稍过一会儿,你就读不懂刚完成的程序了)。从另一个角度看,在设计C++、VB、Perl以及Smalltalk之类的程序设计语言时,设计师也都为解决复杂性问题做了某种程度的工作。并且,正是解决某类特定问题的能力,成就了它们的成功。
随着对Java的了解越来越深,Sun对Java的设计目标给我留下了最深刻印象,那就是:为程序员减少复杂性。用他们的话说就是:“我们关心的是,减少开发健壮代码所需的时间以及困难。”在早期,这个目标使得代码的运行并不快(Java程序的运行效率已经改善了),但它确实显著地缩短了代码的开发时间。与用C++开发相同的程序相比,采用Java只需一半甚至更少的开发时间。仅此一项,就已经能节约无法估量的时间与金钱了。然而Java并未止步于此。它开始着手解决日渐变得重要的各种复杂任务,例如多线程与网络编程,并将其作为语言特性或以工具库的形式纳入Java,这使得开发此类应用变得倍加简单。最终,Java解决了一些相当大的复杂性问题:跨平台编程、动态代码修改,甚至是安全的议题。它让你在面对其中任何一个问题时,都能从“举步维艰”到“起立鼓掌”。抛去我们都能看到的性能问题,Java确实非常精彩地履行了它的诺言:极大地提升程序员的生产率。
同时,Java正从各个方面提升人们相互通讯的带宽。它使得一切都变得更容易:编写程序,团队合作,创建与用户交户的用户界面,在不同类型的机器上运行程序,以及编写通过因特网通信的程序。
我认为,通讯变革的成果并不见得就是传输巨量的比特。我们所看到的真正变革是人与人之间的通讯变得更容易了:无论是一对一的通信,还是群体与群体之间,甚至整个星球之间的通信。我曾听闻,在足够多的人之间的相互联系之上,下一次变革将是一种全球意识的形成。Java说不定就是促进该变革的工具,至少,它所具备的可能性使我觉得,教授这门语言是非常有意义的一件事情。
Java SE5与SE6
本书的第4版得益于Java语言的升级。Sun起初称其为JDK1.5,稍后改作JDK5或J2SE5,最终Sun弃用了过时的“2”,将其改为Java SE5。Java SE5的许多变化都是为了改善程序员的体验。你将会看到,Java语言的设计者们并未完全成功地完成该任务,不过,总的来说,他们已经向正确的方向迈出了一大步。
新版的一个重要目标就是完整地吸收Java SE5/6的改进,并通过本书介绍以及应用这些变化。这意味着本书基本可以称之为“只限Java SE5/6”。并且,书中的多数代码并没有经过老版本的Java编译测试,所以如果你使用的是老版本的Java,编译可能会报错并中止。不过,我觉得这样利大于弊。
如果你不得不采用老版本的Java,我仍然为你在www.MindView.net提供了本书早期版本的免费下载。基于某些原因,我决定不提供本书当前版本的免费电子版。
Java SE6
本书是一个非常耗时的,且具有里程碑意义的一个项目。就在本书出版之前,Java SE6(代号野马mustang)已经发布了beta版。虽然Java SE6中的一些小变化,对书中的代码示例有一点影响,但其主要的改进对本书的绝大部分内容并没有影响。因为Java SE6主要关注于提升速度,以及改进一些(不在本书讨论范围之内)类库的特性。
本书中代码全部用Java SE6的一个发布候选版(RC)进行过测试,因此我不认为Java SE6正式发布时会有什么变化能够影响本书的内容。如果到时真的有什么重要的改变,我将更新本书中的代码,你可以通过www.MindView.net下载。
本书的封面已经指出,本书面向“Java SE5/6”。也就是说本书的撰写“面向Java SE5及其为Java语言引入的重大变化,同时也适用于Java SE6”。
第4版
为一本书写作新版时,作者最满意的是:把事情做得“恰如其分”。这是我从本书上一个版本发布以来所学到的东西。通常而言,这种见识正如谚语所云,“学习就是从失败中汲取教训。”并且,我也借机进行了一些修订。与往常一样,一个新的版本必将带来引人入胜的新思想。此时,新发现带来的喜悦,采用比以往更好的形式表达思想的能力,已经远远超过了可能引入的小错误。
这也是对不断在我脑中盘旋低语着的一种挑战,那就是让持有本书老版本的读者也愿意购买新的版本。这些促使着我尽可能改进,重写,以及重新组织内容,为热忱的读者们献上一本全新的,值得拥有的书。
改变
此版本中将不再包含以往本书中所携带的CD光盘。该CD中的重要部分《Thinking in C》的多媒体教程(由Chuck Allison为MindView创建),现在提供了可下载的Flash版本。该教程是为不熟悉C语法的读者所准备的。虽然,本书用了两章对语法做了较为完整的介绍,然而对于没有相应背景知识的读者而言,这也许仍然不够。而《Thinking in C》正是为了帮助这些读者提升到必要的程度。
完全重写了“并发”这一章(以前称为“多线程”),以符合Java SE5并发类库的重大改变。它将为读者了解并发的核心思想打下基础。如果没有这些核心的基础知识,读者很难理解关于线程的更复杂的议题。我花了很多个月撰写这一章,深陷“并发”的地狱之中,最终,这一章不仅涵盖了基础知识,而且大胆地引入了一些高级议题。
而对于Java SE5所具有的每一个重大的新特性,本书都有一个新的章节与之对应。其他的新特性则加入到了原有的章节中。我还一直在研究设计模式,因此在本书中,也介绍了设计模式的相关内容。
本书经历了重大的重组。这大多源自教授Java的过程,以及我对于“章节”的意义的重新思考。以前,我会不假思索地认为,每个“章节”应该包含一个“足够大的”主题。但是,在我教授设计模式的时候,我发现,如果每次只介绍一个模式(即使讲课的时间很短),然后立刻组织大家做练习,此时那些学员们的表现是最好的(我发现,这种节奏对于我这个老师而言也更有乐趣)。因此,在这一版中,我试着打破按主题划分章节的做法,也不理会章节的长度。我想,这也是一个改进。
我同样也认识到代码测试的重要性。必须要有一个内建的测试框架,并且每次你开发系统时都必须进行测试。否则,根本没有办法知道代码可靠与否。为了做到这一点,我开发了一个测试框架以显示和验证本书中每一个程序的输出结果。(该框架是用Python编写的,你可以在www.MindView.net找到可下载的代码。)关于测试的话题在附录中有讨论,你可以在http://MindView.net/Books/BetterJava找到。其中还包含了其他一些基本技术,我认为所有程序员都应该将它们加入到自己的工具箱中。
此外,我还仔细检查了书中的每一个示例,并且问我自己,“我为什么采用这种方式实现?”对大多数的示例,我都做了一定程度的修订与改进,使得这些示例更加贴切。同时,也传达出我所认为的Java编程中的最佳实践(至少起到抛砖引玉的作用)。许多以前的示例都经过了重新设计与重新编写,同时,删除了不再有意义的示例,也添加了新的示例。
读者们为此书的前三个版本提出了许多许多精彩的意见。这自然使我觉得非常高兴。不过,偶尔读者也会有抱怨,例如有读者埋怨“本书太长了”。对我而言,如果“页数太多”是你唯一的苦恼,那这真令人哭笑不得。(据说奥地利皇帝曾抱怨莫扎特的音乐“音符太多”!我可不是想把自己比作莫扎特。)此外,我只能猜测,发出这种抱怨的读者还不了解Java语言的博大精深,而且也没有看过这一领域的其他书籍。无论如何,在这一版中,我已经删减了过时无用,或不再重要的内容。总的来说,我已经尽我所能仔细复查了全书,进行了必要的增删与改进。对于删除旧的章节,我还是挺放心的。因为原始的材料在网站上都有(www.MindView.net)。本书从第一版到第三版,以及本书的附录,都可以从此网站上下载。
对于仍然不能接受本书篇幅的读者,我向你们道歉。请相信,我已经尽我所能精简本书的长度了。
封面图片的故事
《Thinking in Java》的封面创作灵感来自于美国的 Arts & Crafts运动。该运动始于世纪之交,并在1900 到1920年间达到顶峰。它起源于英格兰,是对工业革命带来的机器产品和维多利亚时代高度装饰化风格的回应。Arts & Crafts强调简洁设计,而回归自然是其整个运动的核心,注重手工制造及推崇个性化设计,可是它并不回避使用现代工具。这和我们现今的情形有很多相似之处:世纪之交,从计算机革命的最初起源到对个人来说更精简、更意味深长的事物的演变,以及对软件开发技能而不仅是生产程序代码的强调。
我以同样的眼光看待Java:尝试将程序员从操作系统机制中解放出来,朝着“软件艺师”的方向发展。
我和封面设计者自孩提时代就是朋友,我们从这次运动中获得灵感,并且都拥有源自那个时期的(或受那个时期启发而创作的)家具、台灯和其他作品。
这个封面暗示的另一主题是一个收集盒,博物学家可以用它来展示他们保存的昆虫标本。这些昆虫可以看作是对象,并放置到“盒”这个对象当中,而盒对象又放置到“封面对象”当中,这形象地说明了面向对象程序设计中最为基本的“集合”概念。当然,程序员可能会不禁联想到“程序缺陷(bug)”;这些虫子被捕获,并假设在标本罐中被杀死,最后禁闭于一个展示盒中,似乎暗示Java有能力发现、显示和制服程序缺陷(事实上,这也是它最为强大的属性之一)。
在本版中,我创造了一幅水彩画,你可以在封面的背景中看到它。
致谢
首先感谢和我一起开研讨课、提供咨询和开发教学计划的这些合作者:Dave Bartlett、Bill Venners、Chuck Allison、Jeremy Meyer和 Jamie King。在我转而不停地竭力为那些像我们一样的独立人群开发在一起协同工作的最佳模式的时候,你们的耐心让我感激不已。
最近,无疑是因为有了Internet,我可以和极其众多的人一起合作,他们协助我一起努力,他们通常是在家办公。过去,我可能必须为这些人提供相当大的办公空间,不过由于现在有了网络、传真以及电话,我不需要额外的开销就可以从他们的帮助中受益。在我尽力学习更好地与其他人相处的过程中,你们都对我很有帮助,并且我希望继续学习怎样使我的工作能够通过借鉴他人的成果而变得更出色。Paula Steuer在接管我偶尔的商务活动时发挥了不可估量的价值,他使它们变得井井有条(Paula,感谢你在我懈怠时对我的鞭笞)。Jonathan Wilcox, Esq.详细审视了我公司的组织结构,推翻了每一块可能隐藏祸害的石头,并且使所有事情都条理化和合法化了,这让我们心服口服。感谢你的细心和耐心。Sharlynn Cobaugh使自己成为声音处理的专家,她是创建多媒体培训CD ROM和解决其他问题的精英成员之一。感谢你在面临难于处理的计算机问题时的坚定不移。在布拉格Amaio的人们也提出了一些方案来帮助我。Daniel Will-Harris最先受到在网上工作的启发,因此他当然是我所有设计方案的主要人物。
多年以来,Gernal Weinberg通过他的学术会议和研讨会,已经成为了我非正式的教练和导师,我十分感谢他。
Ervin Varga在第4版的技术纠正方面提供了巨大的帮助——尽管其他人在各个章节和示例方面也帮助良多,但是Ervin是本书最主要的技术复审者,他还承担了第4版的解决方案指南的重写任务。Ervin发现的错误和对本书所作的完善对本书来说价值连城。他对细节的投入和关注程度令人惊异,他是我所见过的远远超过其他人的最好的技术读者。感谢你,Ervin。
我在Bill Venners的www.Artima.com上的weblog,已经成为了当我需要交流思想时的一种解决之道。感谢那些通过提交评论帮助我澄清概念的人们,包括James Watson、Howard Lovatt、Michael Barker以及其他一些人,特别是那些在泛型方面提供帮助的人。
感谢Mark Welsh不懈的帮助。
Evan Cofsky一如既往地提供了有力的支持,他埋头处理了大量晦涩的细节,从而建立和维护了基于Linux的Web服务器,并保持MindView服务器始终处于协调和安全的状态。
一份特别的感谢要送给我的新朋友,咖啡,它为本项目产生了几乎无穷无尽的热情。当人们来到MindView研讨课时,科罗拉多州Crested Butte的Camp4 Coffee已经成为了标准住所,并且在研讨课中间休息期间,它是我所遇到的最好的饮食场所。感谢我的密友Al Smith,是他使这里成为如此好的一个地方,成为Crested Butte培训期间一个如此有趣和愉快的场所。还要感谢Camp4的所有泡吧常客们,很高兴他们总是为我们提供一些饮料。
感谢Prentice Hall的人们不断地为我提供我所需要的一切,并容忍我所有的特殊需求,而且不厌其烦地帮我把所有事情都搞定。
在我的开发过程中,有些工具已经被证明是无价的;但每次使用它们时都会非常感激它们的创建者。Cygwin (http://www.cygwin.com)为我解决了无数Windows不能解决的问题,并且每天我都会变得更加依赖它(如果在15年前当我的头脑因使用Gnu Emacs而搞得发懵的时候,能有这些该多好啊)。IBM的Eclipse (http://www.eclipse.org)对开发社区做出了真正杰出的贡献,并且随着它的不断升级,我期望能看到它的更伟大之处(IBM是怎样成为潮流所向的?我肯定错过了一份备忘录)。而JetBrains IntelliJ Idea则继续开阔着开发工具的创新之路。
我一开始就将Sparxsystems的Enterprise Architecture用于本书,并且它很快就成为了我选择的UML工具。Marco Hunsicker的Jalopy代码格式化器(www.triemax.com)在大量的场合都派上了用场,而且Marco在将其配置成满足我的特殊需求方面也提供了大量的帮助。我还发现Slava Pestov的JEdit及其插件经常会显得很有用(www.jedit.org),并且对于研讨课来说,它是非常适合初学者的编辑器。
当然,如果我在其他地方强调得还不够的话,我得再次重申,我经常使用Python(www.Python.org)解决问题,在我的密友Guido Van Rossum和PythonLabs那些身材臃肿愚笨的天才人物的智慧结晶的基础上,我花费了好几天的时间进行冲刺(Tim Peters,我现在已经把你借的鼠标加了个框,正式命名为TimBotMouse)。你们这伙人必须到更健康的地方去吃午餐。(还要感谢整个Python社区,他们是一帮令人吃惊的群体。)
很多人向我发送修正意见,我感激所有这些人,第1版特别要感谢:Kevin Raulerson(发现无数的程序缺陷),Bob Resendes(简直难以置信),John Pinto、Joe Dante、Joe Sharp(三位都难以置信),David Combs(校正了许多语法和声明),Dr. Robert Stephenson、John Cook、Franklin Chen、Zev Griner、David Karr、Leander A. Stroschein、Steve Clark、Charles A、Lee、Austin Maher、Dennis P. Roth、Roque Oliveira、Douglas Dunn、Dejan Ristic、Neil Galarneau、David B. Malkovsky、Steve Wilkinson以及许许多多的人。本书第1版在欧洲发行时,Marc Meurrens在电子版宣传和制作方面做出了巨大的努力。
感谢在本书第2版中使用Swing类库帮助我重新编写示例的人们,以及其他助手—Jon Shvarts、Thomas Kirsch、Rahim Adatia、Rajesh Jain、Ravi Manthena、Banu Rajamani、Jens Brandt、Nitin Shivaram、Malcolm Davis,还有所有表示支持的人。
在第4版中,Chris Grindstaff对SWT一节的撰写提供很多帮助,而Sean Neville为我撰写了Flex一节的第一稿。
每当我认为我已经理解了并发编程时,又会有新的奇山险峰等待我去征服。感谢Brian Goetz帮助我克服了在撰写新版本的“并发”一章时遇到的种种艰难险阻,并发现了其中所有的缺陷(我希望如此!)
对Delphi的理解使我更容易理解Java,这一点儿都不奇怪,因为它们有许多概念和语言设计决策是相通的。我的懂Delphi的朋友们给我提供了许多帮助,使我能够洞察一些非凡的编程环境。他们是Marco Cantu(另一个意大利人-难道会说拉丁语的人在学习Java时有得天独厚的优势?)、Neil Rubenking(直到发现喜欢计算机之前,他一直都在做瑜珈/素食/禅道),当然还有Zack Urlocker(最初的Delphi产品经理),他是我游历世界时的好伙伴。我们都很感激Anders Hejlsberg的卓越才华,他在C#领域不懈地奋斗着(正如你将在本书中看到的,C#是 Java SE5主要的灵感之一)。
我的朋友Richard Hale Shaw(以及Kim)的洞察力和支持都很有帮助。Richard和我花了数月时间将教学内容合并到一起,并为参加学习的学生设计出一套完美的学习体验。
书籍设计、封面设计以及封面照片是由我的朋友Daniel Will-Harris制作的。他是一位著名的作家和设计家(http://www.WillHarris.com),在计算机和桌面排版发明之前,他在初中的时候就常常摆弄刮擦信(rub-on letter),他总是抱怨我的代数含糊不清。然而,要声明的是,是我自己制作的照排好的(camera-ready)页面,所以所有排字错误都应该算到我这里。我是用Microsoft Word XP for Windows来编写这本书的,并使用Adobe Acrobat制作照排页面的。本书是直接从Acrobat PDF文件创建而来的。电子时代给我们带来了厚礼,我恰巧是在海外创作了本书第1版和第2版的最终稿—第1版是在南非的开普敦送出的,而第2版却是在布拉格寄出的。第3版和第4版则来自科罗拉多州的Crested Butte。正文字体是Georgia,而标题是Verdana。封面字体是ITC Rennie Machintosh。
特别感谢我的所有老师和我的所有学生(他们也是我的老师)。
Molly,在我从事这一版的写作时总是坐在我腿上,为我提供了她特有的温软而毛茸茸的支持。
曾向我提供过支持的朋友包括(当然还不止他们):Patty Gast(Masseuse extraordinary),Andrew Binstock,SteveSinofsky,JD Hildebrandt,Tom Keffer,Brian McElhinney,Brinkley Barr,《Midnight Engineering》杂志社的Bill Gates,Larry Constantine和Lucy Lockwood,Gene Wang,Dave Mayer,David Intersimone,Chris和Laura Strand,Almquists,Brad Jerbic,Marilyn Cvitanic,Mark Mabry,Dave Stoner,Cranstons,Larry Fogg,Mike Sequeira,Gary Entsminger,Kevin 和Sonda Donovan,Joe Lordi,Dave和Brenda Bartlett,Patti Gast,Blake,Annette&Jade, Rentschlers,Sudeks,Dick,Patty和Lee Eckel,Lynn和Todd以及他们的家人。当然还有我的父亲和母亲。 |
|
读 者 评 论
每个Java程序员都应该反复研读《Think in Java》,并且随身携带以便随时参考。书中的练习颇具挑战性,而有关集合的章节已臻化境!本书不仅帮助我通过了Sun Certified Java Programmer考试,而且它还是我遇到Java问题时,求助的首选书籍。
Jim Pleger, Loudoun郡(弗吉尼亚)政府
这本书比我见过的所有Java书都要好得多。循序渐进……非常完整,并搭配恰到好处的范例,睿智而不呆板的解说……这使本书的品质比别的书“超出了一个数量级”。与其他Java书相比,我发现本书考虑非常周全、前后一致、理性坦诚、文笔流畅、用词准确。恕我直言,这是一本学习Java的理想书籍。
Anatoly Vorobey, 以色列海法Technion大学
在我所见过的程序设计指南中(无论何种语言),这绝对是最好的一本。
Joakim Ziegler, FIX系统管理员
感谢您这本精彩的、令人愉快的Java书。
Dr. Gavin Pillay, 登记员, 南非爱德华八世医院
再次感谢您这本杰出的书。作为一名不用C语言的程序员,我曾经感到(学习Java)步履维艰,但是您的书让我一目了然。能够一开始就理解底层的概念和原理,而不是通过反复试验来自己建立概念模型,真是太棒了。我希望能在不久的将来参加您的讨论课。
Randall R. Hawley, 自动化工程师, Eli Lilly公司
我见过的计算机著作中,这是最好的一本。
Tom Holland
这是我读过的编程语言书中最棒的一本……有关Java的书中最棒的一本。
Ravindra Pai, Oracle 公司, SUNOS 产品线部门
我见过的最好的Java书!您做了一项了不起的工作。您的深度令人赞叹,出版的时候,我一定会购买一本。我从1996年10月就开始学习Java,其间也读过好几本这方面的书,但我觉得您这本才是“必读书”。最近几个月,我一直集中精力于一个完全用Java开发的产品。您的书帮我夯实了某些不牢固的知识点,并拓展了我的知识面。我甚至在面试签约者时引用书中的内容,作为参考的依据。通过问一些我从书中学到的知识,来判断他们对Java的理解程度(例如,数组与Vector的区别)。您的书真是伟大!
Steve Wilkinson, 资深专家, MCI 电信公司
伟大的书。迄今为止我见过的最佳Java书籍。
Jeff Sinclair, 软件工程师, Kestral 计算技术公司
感谢您的《Thinking in Java》。早就应该有人把仅仅介绍语言的教程编写成富有思想、分析透彻的入门指南,而不是局限于“某个公司”的语言。我阅读过许多这方面的书,但只有您和Patrick Winston的作品给我印象深刻。我已经向客户推荐这本书。再次谢谢您。
Richard Brooks, Java 咨询顾问, 达拉斯Sun专业服务部门
Bruce,您的书真是太棒了!您的讲解清晰明确。通过这本迷人的书,我获得了大量Java知识。练习题也同样令人着迷,它们对巩固各章阐述的知识起到了很好的效果。我期待您的更多作品。对您的这本著作致以谢意。阅读了《Thinking in Java》之后,我的代码质量大有改善。为此我要感激您,我相信,维护我的代码的程序员同样也会感激您。
Yvonne Watkins, Discover 技术公司
其他书籍只涵盖Java 的WHAT(探讨语法和相关程序库),或者只包含Java的HOW(实际的程序范例)。《Thinking in Java》则是我知道的书籍中唯一对Java的WHY做出讲解的一本。为什么要这样设计,为什么它会那样运作,为什么有时候会发生问题,为什么它在某些方面比C++好而某些方面不会。虽然它在教授程序语言的WHAT和HOW方面也很成功,但《Thinking in Java》更是爱钻研者的首选Java书籍。
Robert S. Stephenson
感谢您写了一本伟大的书。我越看越喜欢。我的学生也很喜欢。
Chuck Iverson
我要赞美您在《Thinking in Java》一书上的表现。正是有了您这样的人,才使得因特网充满前景,而我想感谢您的付出与努力。真是感激不尽。
Patrick Barrell, Network Officer Mamco, QAF Mfg. Inc.
我真的非常感激您的热情与您的作品。我下载了你的在线书籍的每一个修订版本,我正在深入钻研语言,并探索那些以前从来不敢碰的内容(对于C#、C++、Python和Ruby也有作用)。我至少还有15本Java书(为了工作,我还要掌握JavaScript和PHP语言),并且订阅了《Dr. Dobbs》、《JavaPro》、《JDJ》、《JavaWorld》等杂志。在深入钻研Java(包括Java企业版)之后,我对您的书更加尊敬。它的确是一本有思想的书。我订阅了您的邮件列表,并希望有一天,我所探讨和解决的问题能被您扩展到解题指导中(我将购买解题指导!)。同时,非常感激。
Joshua Long, www.starbuxman.com
市面上的Java书籍,大多比较适合初学者。它们大多数也就只具备基础内容,范例也大同小异。在我见过的富有思想性并讲解高级主题的书籍中,您的是最好的。快点出版吧!……鉴于《Thinking in Java》带给我的深刻印象,我也购买了《Thinking in C++》。
George Laframboise, LightWorx 技术咨询公司
关于您的《Thinking in C++》(我工作的时候,它总在书架上占据最显眼的位置),我曾经写信告诉过您我对它的喜爱。现在,我通过您的电子书仔细钻研Java,我还得说“我喜欢!”本书内容广博,讲解详细,阅读起来不像是无味的教科书。您的书中涵盖了Java 开发工作中最重要、却很少被提及的概念—“原理”。
Sean Brady
我同时用Java和C++进行开发,您的这两本书是我的救星。如果我被某个问题难住了,我知道可以靠您的书来:a) 清楚地解释原因;b) 找到符合我所遇问题的具体例子。我还没找到另一位能令我如此反复热情推荐的作者(如果有人愿意听我推荐的话)。
Josh Asbury, A^3 软件咨询公司, 辛辛那提, 俄亥俄
您的例子不仅清楚,而且容易理解。Java中的许多重要细节您都考虑到了,这些内容在编排较差的Java文档中很难找到。您假设程序员已经具有了基本知识,这就节约了读者的时间。
Kai Engert, 德国Innovative 软件公司
我是《Thinking in C++》一书的忠实书迷,我已经将它推荐给了我的同事们。当我读完您的Java书籍电子版时,我觉得,您总能保持高水准的写作水平。感谢您!
Peter R. Neuwald
写得非常好的Java书……我认为您在此书上取得了非常出色的成就。作为芝加哥地区Java兴趣小组的领导人,我已经多次在我们最近的聚会中赞扬您的这本书和您的网站。我想将《Thinking in Java》作为我们每月聚会讨论的主要内容。这样我们可以在聚会中对书中的章节进行复习和讨论。
Mark Ertes
顺便提一下,《Thinking in Java 2nd Edition》俄语版依旧畅销。阅读此书已经与学习Java成为同义词,真是太好了。
Ivan Porty(《Thinking In Java 2nd Edition》俄语版的译者及出版商)
对于您的辛勤工作,我由衷感激。您的书是佳作,我将这本书推荐给我们这儿的使用者和博士班学生。
Hugues Leroy // Irisa-Inria Rennes France,
Head of Scientific Computing and Industrial Tranfert
虽然我只读了约40 页的《Thinking in Java》,却已经发现本书是我所见过的讲述最为清晰、编排最为合理的程序设计书籍……作为一名作者,我可能会有些挑剔。我已经订购了《Thinking in C++》,迫不及待地想钻研一番。对于程序设计,我还算是新手,因此事事都得学习。这不过是一篇向您的绝佳作品致谢的简短书信。在痛苦地遍览大多数语言艰涩、内容散乱的计算机书籍(包括那些有着极佳口碑的书籍)后,我对计算机书籍的阅读热情一度消退。不过,现在我又重拾信心。
Glenn Becker, Educational Theatre Association
感谢您提供了这么一本精彩的书。当我遇到那些令人困惑的Java 和C++问题时,这本书对我最终理解问题提供了极大帮助。阅读您的书令人如沐春风。
Felix Bizaoui, Twin Oaks Industries, Louisa, Va.
对于这部优秀的作品,我必须向您道贺。鉴于阅读《Thinking in C++》的经验,我决定读一读《Thinking in Java》,而事实证明它的确未让人失望。
Jaco van der Merwe, 南非DataFusion系统公司软件专家
本书无疑是我所见过的最佳的Java书籍之一。
E.F. Pritchard, 英国剑桥动画系统公司高级软件工程师
您的书使那些我曾经读过或草草翻过的Java 书显得更加无用、该骂。
Brett g Porter, Art & Logic公司高级程序员
我阅读您这本书已经一两个星期了。与以前我曾读过的Java 书籍比较,您的书似乎更能给我一个绝佳的开始。我已经把此书推荐给我的朋友们,他们对此书也评价甚高。对于您写出的这本著作,请接受我的恭喜。
Rama Krishna Bhupathi, 加州圣何塞TCSI 公司软件工程师
只是很想告诉您,您这本书是多么杰出的作品。我已将它作为公司内部Java工作的主要参考书。我发现目录的安排恰如其分,可以很快找到需要的章节。能够看到这么一本既不拿API炒冷饭,也不把程序员当傻瓜的书,真是太棒了。
Grant Sayer, 澳大利亚Ceedata 系统私人有限公司,Java组件组长
哇!这是一本可读性强、极富深度的Java书籍。市面上已经有太多质量低劣的Java 书籍。其中虽然也有少数不错的,但在看过您的大作之后,我认为它当然是最好的。
John Root, 伦敦社会安全局Web开发人员
我才刚开始阅读《Thinking in Java》。我想它一定相当不错,因为我很喜欢《Thinking in C++》(我以一名熟悉C++、同时希望提升自身能力的程序员身份来阅读这本书)。尽管我不太熟悉Java,但本书想必能令我满意。您是一位伟大的作家。
Kevin K. Lewis, ObjectSpace公司技术专家
我想这是一本了不起的书。我所有的Java知识都学自这本书。感谢您让大家可以从Internet上免费取得这本书。如果没有您的付出,我至今恐怕仍然对Java一无所知。本书最棒的一点,莫过于它同时也说明了Java 不好的一面,而不像那些商业宣传资料。您的表现真是优秀。
Frederik Fix, 比利时
我始终热中读您的著作。几年以前,当我开始学习C++时,是《C++ Inside & Out》带领我进入C++的迷人世界。那本书帮助我得到了更好的机会。现在,为了更进一步钻研知识,我兴起了学习Java 的念头,我无意中又碰见了《Thinking in Java》。毫无疑问,我认为自己不再需要其他书籍。它是那么的令人难以置信。阅读此书的过程,就像重新发掘自我一样。我学习Java至今只有一个月,现在对Java的体会日益加深,这一切都不得不由衷感谢您。
Anand Kumar S., 印度Computervision公司软件工程师
您的书作为综合性的导论,是如此出色。
Peter Robinson, 剑桥大学计算机实验室
在帮助我学习Java的书籍中,这一本显然是最好的。我只是想让您知道,我觉得自己能够读到这本书是多么幸运。谢谢!
Chuck Peterson, IVIS 国际公司Internet产品线产品组长
了不起的一本书。自从我开始学习Java,这已经是第三本了。目前我大概阅读了三分之二,并打算把它读完。我能够找到这本书,是因为这本书被用于Lucent 技术公司的某些内部课程,而且有个朋友告诉我这本书可以在网络上找到。很棒的作品。
Jerry Nowlin, Lucent 技术公司MTS部门
在我所读过的六本Java书籍中,您的《Thinking in Java》显然最好,也最清晰易懂。
Michael Van Waas博士, TMR Associates公司总裁
感谢您的《Thinking in Java》。您的作品真是精彩!更不必说它可以免费从网络下载了!作为一名学生,我觉得您的书籍是无价之宝(我也有一本《C++ Inside & Out》,它同样是一本伟大的C++书籍),因为您的书不仅教导我应该怎么做,也教导我这么做的原因所在,这一点对C++或Java学习者建立起坚固基础非常重要。我有许多和我一样喜爱程序设计的朋友,我也对他们提起您的书。他们觉得真是太棒了!再次谢谢您!顺道一提,我是印度尼西亚人,就住在“爪哇”(Java)。
Ray Frederick Djajadinata, 印度尼西亚雅加达Trisakti 大学学生
单是将作品免费放在网络上这种气度,就令我震惊不已。我想,我应该让您知道,对于您的工作,我是多么感激与尊敬。
Shane LeBouthillier, 加拿大 Alberta大学计算机工程系学生
我得告诉您,每个月我都在期待您的专栏。作为面向对象程序设计领域的新手,我要感谢您花在那些基础主题上的时间和思考。我已经下载了您的这本书,而且我一定会在本书出版的时候购买一本。感谢您对我的帮助。
Dan Cashmer, B. C. Ziegler公司
能够完成这么了不起的作品,恭喜您。开始,我偶然发现了《Thinking in Java》的PDF版本。甚至在我读完之前,我又跑到书店找到了《Thinking in C++》。我已经在计算机领域工作了八年多,做过顾问、软件工程师、教师/教练,最近则从事自由职业。所以我觉得自己也算是见多识广了(注意,不是“无所不知”,而只是“见多识广”)。不过,这些书使得我的女朋友称我为“呆子”。我并不反对,只不过我发现自己已经远远超过这个阶段。我发现自己如此喜爱这两本书,我以前接触过或购买过的其他计算机书籍,都无法与之相比。这两本书都有极佳的写作风格,对于每个新主题都有很好的介绍,书中充满了睿智的见解。干得好。
Simon Goland, simonsez@smartt.com, Simon Says 咨询公司
我得说,您的《Thinking in Java》真是了不起。它正是我要找的那种书。尤其那些讨论优秀与拙劣的Java 软件设计的章节,完全就是我想要的。
Dirk Duehr, 德国贝塔斯曼集团 Lexikon 公司
感谢您写了两本著作:《Thinking in C++》和《Thinking in Java》。在面向对象程序设计的学习过程中,您带给我巨大帮助。
Donald Lawson, DCL Enterprises公司
感谢您花时间来撰写这么一本很有用的Java书籍。如果是教学让您明白了某些事情的话,到如今您一定极为满意自己的成就。
Dominic Turner, GEAC Support
我曾读过的最棒的Java 书籍—我真的读过不少。
Jean-Yves MENGANT, 法国巴黎 NAT-SYSTEM公司首席系统架构师
《Thinking in Java》涵盖全面,讲解清晰。本书极易阅读,而且程序代码也是如此。
Ron Chan博士, 匹兹堡 Expert Choice公司
您的书真好。我读过许多程序设计书籍,但是这本书中您对程序设计的深刻见解依然深深触动了我。
Ningjian Wang, Vanguard 集团信息系统工程师
《Thinking in Java》是一本既优秀,又容易阅读的书籍。我向所有的学生推荐它。
Dr. Paul Gorman, 新西兰 Otago大学计算机科学系
依靠您的书,我现在已经理解了面向对象程序设计的含义……我相信,Java比Perl更直接,甚至更容易。
Torsten R歮er, Orange 丹麦公司
您打破了“天下没有白吃的午餐”这句谚语。不是那种施舍性质的午餐,而是连美食家都觉得美味的午餐。他们都会为此感激您。
Jose Suriol, Scylax 公司
感谢有机会看到这本书成为一部杰作!在这个主题上,本书绝对是我所读过的最佳书籍。
Jeff Lapchinsky, Net Results 技术公司程序员
您的书简明扼要,容易理解,而且读起来充满乐趣。
Keith Ritchie, KL 集团公司Java研发组
确实是我所读过的最好的Java 书籍!
Daniel Eng
生平所见最好的Java 书籍!
Rich Hoffarth, West 集团高级架构师
感谢您带来了如此精彩的一本好书。通读各个章节带给我极大的乐趣。
Fred Trimble, Actium 公司
您一定掌握了艺术的精髓,使我们得以循序渐进地成功掌握细节知识。您也让学习过程变得非常简单,同时令人愉快。感谢您这本真正精彩的指南。
Rajesh Rau, 软件顾问
《Thinking in Java》撼动了整个自由世界!
Miko O誗ullivan, Idocs 公司总裁
关于《Thinking in C++》
最好的书!1995年《Software Development 》杂志Jolt大奖得主!
“本书成就非凡。您应该在书架上也摆一本。其中讨论输入、输出流的章节,在我所见过的有关此主题的论著中,它是表述最全面、也最容易理解的。”
Al Stevens, 《Dr. Dobbs Journal》的特约编辑
“对于如何重新认识面向对象程序的构造,Eckel的这本书是唯一能做出如此清晰解释的书籍。同时,它也是透彻讲解C++的优秀教程。”
Andrew Binstock, 《Unix Review》的编辑
“Bruce对C++的洞察力,不断令我感到惊讶。《Thinking in C++》则是他迄今为止所有绝妙想法的最佳合集。有关C++的种种难题,如果您需要清楚的解答,请买下这本杰作。”
Gary Entsminger, 《The Tao of Objects》的作者
《Thinking in C++》耐心而系统地对C++种种特性的使用时机与方式进行了探讨。包括:内联函数、引用、操作符重载、继承、动态对象。也包括了许多高级主题,比如模板、异常、多重继承的恰当用法。对这些交织在一起,最后形成了Eckel对对象和程序设计的独特看法。它是每个C++开发者书架上的必备好书。如果您正以C++从事严肃的开发工作,那么《Thinking in C++》是您的必备书籍之一。
Richard Hale Shaw, 《PC Magazine》的特约编辑
题 献
献 给 Dawn |
|
|
|
|