
本书范例 PraticalJavaCode.zip
□中译书名(暂名):Practical Java 中文版
□适合对象:中高阶 Java 程式员。
□制作特色:与原文本页页对译,含 index,网片输出,平装。
□内容特色:68 条 Java编程准则与注意事项
开放档案如下:
| 档名 | 内容 | 大小 bytes |
| practical-java-chap1-3.pdf 不需密码即可开启。 |
扉页、译序、目录、细目、前言、致谢、1-3章 | |
| PraticalJavaCode.zip | 本书范例源码 (原作者提供) |
139,288 |
不需密码即可开启。档案不含书签(目录连结)。
如欲下载,请将滑鼠移至上述 hyperlink,按右键,再选【另存目标...】即可。
译序
Java是一门优秀的物件导向编程语言(Object Oriented Programming Language, OOPL)。什麽是「物件导向」?如何才称得上「优秀」?前者可定量定性,客观;後者往往流於个人感受,主观!所以虽然物件导向语言有着几近一致的条件和门槛
(注1)(封装、继承、多型┅),孰优孰劣却是各人心中一把尺。尽管如此,无人可以否认Java语言在OOP(物件导向编程)上拥有良好的特性和优越的表现。 注1:我常忆起网络论坛上时可与闻的一种怪诞态度。有一派人士主张,OO是一种思想,一种思考模式,任何语言都能够实现它,因而侈言「C或assembly语言也能OO」。任何语言各有用途,这是完全正确的;OO是一种思维,这话也是对的。任何语言都能够实现OO,这话对某些人也许是对的,对99.9999%的人是错的。以non-OO语言实现OO思维,非但达成度极低,也非人人能为。Edmund Hillary(艾德蒙 希拉瑞)能达到的高度,你未必达得到 ─ 事实上你通常达不到。(注:Edmund Hillary是第一位登上圣母锋的地球人,1953年英格兰远征队员。)我所谓良好的
OOP特性,指的是Java提供了许多让程式员得以轻松表达物件导向技术与思维的语言关键字(keywords)如class, abstract, interface, extends, implements, public, protected, private, final, static, finalize┅,又提供条理清晰结构分明的档案组织方式如package, import,又拥有严谨而灵活的动态型别系统(dynamic type system)使得以提供RTTI和Reflection机制,并拥有一个优秀、涵盖面广、扩充性强的标准程式库(Java Libraries)。这些优秀的语言构件(
constructs)虽然好用易用,但不论就技术面或应用面或效率考量,还是有许多隐微细节散布其中,例如object creation, object initialization, Cloneable, Serializable, Equality, Immutability, Multithreading (Synchronization), Exception Handling┅,在在需要Java程式员深入认识与理解。市面上
Java书籍极多,专注於「编程主题式探讨」并「以独立条款呈现」的书籍比较少。这类书籍面向中高阶读者,不仅选题必须饶富价值、探讨必须极为深刻,各主题最好还独立以利选择阅读,却又最好彼此前後呼应环环相扣,并附良好交叉索引,予读者柳暗花明的强烈冲击。此种「专题条款」式的表现风格,在Scott Meyers的《Effective C++》和《More Effective C++》二书面世之後获得许多赞扬,也引来许多追随。《
Practical Java》和《Effective Java》二书,对前述重要而基础的技术细微处有着详尽、深刻、实用的介绍和剖析和范例,又以独立条款之姿展现,在内容的扎实度、可读性、易读性上表现均十分良好。为此,秉持并承继我为C++ 社群翻译《Effective C++》、《More Effective C++》的态度和机缘,我很开心再次由我负责,将《Practical Java》和《Effective Java》二书中译本呈献给Java 社群。考虑本书读者应已具备
Java编程基础,对於各种英文术语已有良好的接受度,我在书中保留了许多英文术语,时而中英并陈,包括class, object, interface, reference, instance, array, vector, stack, heap┅,也包括涉及Java关键字的一些用语如private, public, protected, static, abstract┅,不胜枚举(下页另有一个扼要说明)。本书努力在字型变化上突显不同类形的术语,以利读者阅读。本书支援网站有一个「术语 英中繁简」对照表,欢迎访问,网址如下。《
Practical Java》由刘永丹先生和我合力完成。永丹做前期初译工作,我负责後继的文字修润、技术检阅、大局风貌。永丹技术扎实,文字用心。没有他的协助,本书不可能在这个时间以这样的品质面世。谢谢永丹。本书每一章起始处都有作者匠心独具收集的一些文摘语录。我们虽勉力译出,恐见识不足,贻笑大方,故均留下原文和出处,庶几不误读者。
侯捷
2003/07/08 于台湾.新竹jjhou@jjhou.com(电子邮箱)
http://www.jjhou.com(繁体)(术语对照表http://www.jjhou.com/terms.htm)
http://jjhou.csdn.net(简体) (术语对照表http:// jjhou.csdn.net/terms.htm)
p.s. 本书已就英文版截至2003/07/08之勘误表修正於纸本。
■本书术语翻译与保留之大致原则:
※
广被大众接受之术语,无需额外说明,不在此列。例如继承(inheritance)、封装(encapsulation)、多型(polymorphism)。※
本书保留与Java关键字相关之术语不译,例如class, interface, private, public, protected, static, final, abstract, synchronized, serializable┅。※
本书保留资料结构名称不译,例如array, vector, list, map, set, stack, heap┅。"collection" 译为「群集」。※
"class" 及其所衍生之各种名词如subclass, superclass, immutable class, mutable class, base class, derived class等皆保留不译(时而英中并陈)。"object" 大多数时候译为「物件」,时而保留。"object reference" 保留不译,"reference" 亦不译。※
"type" 译为「型别」。"parameter" 译为「叁数」,"argument" 译为「引数」。"delegate", "delegation" 译为「委托」,"aggregate", "aggregation" 译为「聚合」。"composition" 译为「复合」。※
动词 "create" 译为「创建」或「建立」,描述物件之初次诞生。动词 "refer" 译为「指涉」或「指向」或「引用」。动词 "dereference" 译为「提领」。动词 "override" 译为「覆写」。动词 "overload" 译为「重载」。※
本书将Java class "methods" 译为函式,因为它等价於其他编程语言之 "function"。若直译为「方法」,行文缺乏术语突出感,恐影响阅读流畅;若不译,过於频繁出现又恐影响版面观感。※
本书将Java class "fields" 译为栏位,等价於C++ 语言之 "data member"。※
本书将 "clone" 译为「克隆」(这一用词在中国大陆极为普遍),映照 "copy" 之於「拷贝」。非单纯保留 "clone" 是因为它时常做为动词并频繁出现,而我对术语的保留态度是尽量只考虑名词(偶有形容词)。※「
static栏位与instance栏位」、「reference物件与value物件」、「reference型别与primitive型别」等等术语保留部分英文,并使用特殊字型。※
本书支援网站有一个「术语英中繁简」对照表,欢迎访问,网址见上页。※
术语翻译有许多两难之处,祈愿读者体谅;译者勉力求取各方平衡,并尽可能於突兀处中英并陈。-- 侯捷
细目
实践
1:引数是以传值(by value)而非传址(by reference)方式传递所有
Java objects都透过object reference而被取用。常见的一个误解是Java以实践
2:对不变的data和object reference使用final为了让
data或object reference成为不变量,请使用实践
3:预设情况下所有non-static函式都可被覆写(overridden)预设情况下,所有
non-实践
4:慎重选择arrays和Vectorsarrays和vectors是常见的容器类别(storage classes
实践
5:多型(polymorphism)优於instanceofinstanceof
的许多用途可以因为改用多型而消除之。使用多型,程式码将更清晰、更易於扩展和维护。实践
6:必要时才使用instanceof有时我们无法回避使用
实践
7:一旦不再需要object references,就将它设为null不要忽视记忆体可能带来的问题。尽管有了垃圾回收机制(
garbage collection),你仍然需要关注你的程式码如何运用记忆体。如果能够领悟垃圾回收机制和记忆体运用细节,你就能够更好地知道何时应该将object references设为null,那将导致高效的程式码。实践
8:区分reference type和primitive typeJava是物件导向的,但其操控的东西并非都是物件(objects)。理解reference type和primitive types之间的差异,及它们在JVM中的表述(representation),会使你在运用它们时得以做出明智的选择。
实践
9:区分 == 和equals()==
用来测试基本型别的相等性,亦可判定两个object references是否指向同一个object。若要测试values(值)或semantic(语意)相等,应使用equals()。实践
10:不要依赖equals()的预设实作(default implementation)不要不假思索地认定一个
class总是会正确实作出实践
11:实作equals()时必须深思熟虑如果某个
class的两个objects「即使不占用相同的记忆体空间,也被视为逻辑上相等」,那麽就该为这个class提供一个实践
12:实作equals()时优先考虑使用getClass()实作
实践
13:呼叫base class(基础类别)的super.equals()任何
base class(除了实践
14:在equals()函式中谨慎使用instanceof唯有当你考虑允许「一个
derived class object可以相等於其base class object」时,才在实践
15:实作equals()时需遵循某些规则撰写
实践
16:认识「异常控制流」(exception control flow)机制让自己谙晓异常控制流程细节。了解这些细微之处有助於你回避问题。
实践
17:绝对不可轻忽异常(Never ignore an Exceptions)一旦异常出现却没有被捕获,抛出异常的那个执行绪(
thread)就会中止运行。是的,异常意味错误,永远不要忽略它。实践18:千万不要掩盖异常(Never hide an Exceptions)
如果处理异常期间又从
catch或finally区段抛出异常,原先的异常会因而被隐蔽起来。一旦发生这样的事情,就会丢失错误资讯。你应当撰写专门负责处理这种情形的程式码,将所有异常回传给呼叫者。实践19:明察throws子句的缺点
将一个异常加入某函式的
throws子句,会影响该函式的所有呼叫者。实践20:细致而全面地理解throws子句
任何函式的
throws子句应当列出它所传播的所有异常,包括衍生异常型别(derived exception types)。实践
21:使用finally避免资源泄漏(resource leaks)不要忽视记忆体以外的资源。垃圾回收机制不会替你释放它们。请使用
实践
22:不要从try区块中回返不要从
实践
23:将try/catch区块置於回圈(loop)之外撰写含有异常处理的回圈时,请将
实践
24:不要将异常(exceptions)用於流程控制请将异常用於预期行为之外的情况。不要以异常来控制流程,请采用标准的语言流程构件(
flow constructs),这样的流程表达会更清晰更高效。实践
25:不要每逢出错就使用异常(exceptions)只有面对程式行为可能出乎预料的情境下才使用异常。「预期中的行为」应使用回传码(
return codes)来处理。实践26:在建构式(constructors)中抛出异常
尽管建构式并非函式(
method),因而不能回传一个值,但建构式有可能失败。如果它们失败了,请抛出一个异常。实践27:抛出异常(exceptions)之前先将object恢复为有效状态
抛出异常很容易,困难的是「将异常所引发的伤害减到最小」。抛出异常之前,应确保「如果异常被处理好,流程再次进入抛出异常的那个函式中,该函式可以成功完成」。
实践
28:先把焦点放在设计、资料结构和演算法身上给
Java带来最大效能提升的办法就是:在设计和演算法中使用与语言无关的技术。因此,首先请将你的精力集中於这些上面。实践
29:不要倚赖编译期程式码优化技术由
Java编译器生成的码,通常不会比你自己撰写的更好。别指望编译器能够多麽优化你的原始码。实践30:理解运行期(runtime)程式码优化技术
Java效能的大部分努力都围绕着运行期优化展开。这种作法有利有弊。
实践31:如欲进行字串接合,StringBuffer优於String
对於字串接合,
StringBuffer class要比String class快许多倍。实践32:将object的创建成本(creation cost)降至最小
在许多物件导向系统中,「产生物件」意味着高昂的成本。了解成本所在,以及了解「加速物件产生速度」的技术,都可以导致更快的程式码。
实践33:慎防未用上的物件(unused objects)
非必要别产生物件。非必要地产生物件,会减慢你的程式速度。
实践34:将同步(synchronization)减至最低
宣告
synchronized函式或synchronized区块,会显着降低效能。只在物件需要时才使用同步机制(synchronization)。实践35:尽可能使用stack变数
stack变数为JVM提供了更高效的byte code指令序列。所以在回圈内重复取用static变数或instance变数时,应当将它们临时储存於stack变数中,以便获得更快的执行速度。
实践36:使用static、final和private函式以允许实施inlining
以函式本体替换函式呼叫,会导致更快的程式码。如果要令函式为
inline,必须先宣告它们为static、final或private。实践37:instance变数的初始化只要一次就好
由於所有
static变数和instance变数都会自动获得预设值,所以不必重新将它们设为预设值。实践38:使用基本型别(primitive types)使程式码更快更小
使用基本型别,比使用基本型别外覆类别(
wrapper),产生的程式码又小又快。实践39:不要使用Enumeration或Iterator来巡访Vector
巡访
Vector时,请使用get函式而非Enumeration或Iterator。这样做会导致更少的函式呼叫,意味程式码会更快。实践40:使用System.arraycopy() 来复制arrays
请使用
System.arraycopy() 来复制arrays。那是个原生(native)函式,速度最快。实践41:优先使用array,然後才考虑Vector和ArrayList
如果可能,就使用
array。如果你需要Vector的功能但不需要它的同步特性,可改用ArrayList。实践42:尽可能复用(reuse)objects
复用现有物件,几乎总是比产生新物件更划算。
实践43:使用缓式评估(延迟求值,lazy evaluation)
如果某个成本高贵的计算并非一定必要,就尽量少做。请使用「缓式评估」
(
实践44:手工优化(optimize)你的程式码
由於
Java编译器在优化方面的作为甚少,为了生成最佳byte code,请手工优化你的原始码。实践45:编译为原生码(native code)
编译为原生码,通常可以导致执行速度更快的程式码。但你却因此必须在各种不同的原生方案(
native solution)中取舍。实践
46:面对instance函式,synchronized锁定的是物件而非函式或程式码实践
47:弄清楚synchronized statics与synchronized instance函式之间的差异两个函式被宣告为
synchronized,并不就意味它们是「执行绪安全」(thread-safe)的。对instance函式或object reference同步化,与对static函式或class literal(字面常数)同步化相比,得到的lock全然不同实践
48:以「private资料搭配存取器(accessor)」取代public/protected资料如果没有适当保护你的资料,用户便有机会绕过你的同步机制。
实践
49:避免无谓的同步控制(avoid unnecessary synchronization)一般情况下请不要同步化所有函式。同步化不仅造成程式缓慢,并且丧失了
并行(
实践
50:取用共享变数时请使用synchronized或volatile不可切割(原子化,
atomic)操作并非意味「执行绪安全」。JVM实作品被允许在私有记忆体中保留变数的工作副本。这可能会产生陈旧数值(stale values)。为避免这个问题,请使用同步化机制或将变数宣告为实践
51:在单一操作(single operation)中锁定所有用到的objects同步化某一函式,并不一定就会使其成为「执行绪安全」的函式码。如果
实践52:以固定而全域性的顺序取得多个locks(机锁)以避免死结(deadlock)
实践53:优先使用notifyAll()而非notify()
notify()
只唤醒一个执行绪。要想唤醒多个执行绪,请使用notifyAll()。实践
54:针对wait()和notifyAll()使用旋锁(spin locks)当你等待条件变数(
condition variables)时,请总是使用旋锁(spin locks)以确保正确结果。实践
55:使用wait()和notifyAll()取代轮询回圈(polling loops)将所有
polling loops替换为使用实践
56:不要对locked object(上锁物件)之object reference重新赋值当一个
object被锁定,有可能其他执行绪会因同一个object lock而受阻(blocked)。假如你对上锁物件的object reference重新赋值,其他执行绪中悬而未决的那些locks将不再有意义。实践
57:不要呼叫stop()或suspend()不要呼叫
实践
58:透过执行绪(threads)之间的合作来中止执行绪你不应该呼叫
实践
59:使用interface支援多重继承(multiple inheritance)当你想要支援
实践
60:避免interfaces中的函式发生冲突没有任何办法能够阻止两个
interfaces使用同名的常数和函式。为了避免可能的冲突,应当小心命名常数和函式。实践61:需要提供部分实作(partial implementation)时,请使用abstract classes
实践62:区分interface、abstract class和concrete class
一旦正确理解了
interface、abstract class和concrete class的差异,你就可以在设计和撰码时做出正确的选择。实践63:审慎地定义和实作immutable classes(恒常类别)
如果你希望
object的资料永远不被改动,请使用immutable object。这种objects自动拥有执行绪安全性(thread safety)。实践64:欲传递或接收mutable objects(可变物件)之object references时,请施行clone()
为了保证
immutable objects,你必须在传入和回传immutable objects时对它们进行clone动作。实践65:使用继承(inheritance)或委托(delegation)来定义 immutable classes(恒常类别)
使用
immutable interface、common interface或base class,或是immutable delegation classes,来定义immutable classes(恒常类别)。实践66:实作clone()时记得呼叫super.clone()
当你实作了一个
clone(),总是应该呼叫super.clone()以确保产生正确的object。实践67:别只是倚赖finalize()清理记忆体以外的(non-memory)资源
你不能保证
finalize()是否被呼叫,以及何时被呼叫。因此,请专门实作一个public函式来释放记忆体以外的资源。实践68:在建构式(constructors)内呼叫non-final函式时要当心
如果一个
non-final函式被某个derived class覆写,在建构式中呼叫这个函式可能会导致不可预期的结果。
本书汇集了
Java编程实践方面的建议、忠告、范例和讨论。本书的组织是一个个独立课程,每个课程谓之实践(PRAXIS,发音prak-sis),用以讨论特定主题。每个实践按各自独立的方式撰写。你可以从头阅读到尾,也可以挑选某些专题阅读。这种编排风格使你可以在短暂的闲暇中阅读此书。许多实践都少於5页,因此你可以在简短的时间内学习它们。我在这本书中详细分析了某些设计(
design)和编程(programming)方面的问题。我挑选主题的依据是编程实践上的有效(effective)和高效(efficient)性质。Java最被人抱怨的一点是效能(效率,performance),因此我以最大的篇幅讨论这一主题,探索使Java程式码执行得更有效率的技术。我撰写本书,希望它能够作为指南,帮助你设计和撰码。它可以帮助你更全面地理解
Java,让你撰写出更高效、更健壮和(或许最重要的是)更正确的程式码。本书所有资讯都适用於各种
Java编程,并不囿於伺服端(server)、客户端(client)或GUI(图形用户介面)编程。此外,你可以将这些资讯运用於Java的任一发行版本。本书风格受了
Scott Meyers所着的《Effective C++》和《More Effective C++》的影响。我发现他的风格对书籍组织非常有益,因此我决定采用类似的格式。预期读者
本书是为已经掌握
Java语言基础知识的程式员准备的。我假设读者已经具备Java语言和并行编程(concurrent programming)的工作经验,并理解物件导向(object-oreinted)的概念和术语。本书适用於「想获得如何高效使用Java之实用建议、讨论和范例」的程式员。无论对
Java编程老手或新手,本书都为他们提供了Java关键领域的资讯和讨论。本书提供充足的新资讯,即使经验丰富的程式员也能从考查他们业已熟悉的领域中获得极大收益。例如在某些场合,我以独特的方式讨论问题,帮助程式员以不同的方法思考,或使用与以往不同的角度看待事物。初入门的程式员也可以从本书获益良多。我提供了讨论和范例,帮助他们消除许多常见的编程错误。我也澄清了某些常见的
Java错误观念,并强调语言特性方面的某些问题。本书组织
本书组织为六大部分。
在上述各标题之下,是数量不等的相关专题。往往我会在不止一处讨论特定专题的某个属性。例如我在不同场合讨论了
synchronized关键字的方方面面,每次讨论都涉及synchronized的不同特性。为此,我提供了扩展性甚强的交叉叁照,你可以由此得知何时阅读特定专题、何处存在相关资讯。目录之後便是细目。这一部分包含所有实践标题和其页码,并附有每个实践的核心概要。你可以使用这个细目唤起你对专题的记忆,或用以找出某个专题。
附录内含一个已经证实的技术,可以进一步扩展你的
Java知识。之後还有一份「进一步阅读」列表,列出关於Java、一般设计和编程方面的书籍和期刊。三言两语话
PRAXIS(实践)PRAXIS
(实践)一词,是我查询「得以概括本书所做工作」的词汇的结果。1982年的《American Heritage Dictionary》将PRAXIS定义为:The practical application or exercise of a branch of learning(实际应用或训练;学习的一个支脉)。这正是我希望在本书中达成的。最确切的恐怕是《
Webster's New Collegiate Dictionary》於1958年给出的一份定义:Practice, especially of an art, science, or technical occupation; opposite to theory(实际履行,尤指艺术、科学或技术领域;与理论遥相对应)。这个定义准确概括了本书精髓。那句opposite to theory更是画龙点睛。「理论」本身当然没错,但本书没有为它准备位置。范例程式码
正文所列的所有程式码,都采用本书写作时可获得之
Java最新版本加以编译和运行。所有程式码都曾经在Windows NT 4.0环境下以Sun Java 2 SDK,Standard Edition,v1.2.1完成编译和运行。如果你想要得到原始码,请在以下网址进行注册:http://www.awl.com/cseng/register
该网页要求你输入一个独一无二的码,此码可在本书末尾标明为「
How to Register Your Book」的页面找到。提供反馈
欢迎读者对本书提供相关反馈资讯。任何建议、批评或臭虫报告,都请寄到
PracticalJava@awl.com。希望本书让你觉得有用、可读,并且具备实用价值。
Peter Haggar
Research Triangle Park, North Carolina
November, 1999