Practical Java

原创 2005年05月14日 03:30:00

《Practical Java》
中文版 (繁体)
侯捷 / 刘永丹 译

68 条 Java编程准则与注意事项

细目

理论上,理论和实际并没有区别;实际上,它们是有区别的。
In theory, there is no difference between theory and practice. But, in practice, there is.
Jan L.A. van de Snepscheut

一般技术(General Techniques)

实践1:引数是以传值(by value)而非传址(by reference)方式传递

所有Java objects都透过object reference而被取用。常见的一个误解是Java以by reference方式传递引数。事实上所有引数都以by value方式传递。

实践2:对不变的data和object reference使用final

为了让data或object reference成为不变量,请使用final。注意,final仅仅令object reference自身成为不变量,并不限制它所指向之物件的改变。

实践3:预设情况下所有non-static函式都可被覆写(overridden)

预设情况下,所有non-static函式都可以被subclass覆写。但如果加上关键字final,便可防止被subclass覆写。

实践4:慎重选择arrays和Vectors

arrays和vectors是常见的容器类别(storage classes)。选用它们之前应该先了解它们的功用和特性。

实践5:多型(polymorphism)优於instanceof

instanceof的许多用途可以因为改用多型而消除之。使用多型,程式码将更清晰、更易於扩展和维护。

实践6:必要时才使用instanceof

有时我们无法回避使用instanceof。我们应该了解什麽情况下必须使用它。

实践7:一旦不再需要object references,就将它设为null

不要忽视记忆体可能带来的问题。尽管有了垃圾回收机制(garbage collection),你仍然需要关注你的程式码如何运用记忆体。如果能够领悟垃圾回收机制和记忆体运用细节,你就能够更好地知道何时应该将object references设为null,那将导致高效的程式码。

物件与相等性(Objects and Equality)

实践8:区分reference type和primitive type

Java是物件导向的,但其操控的东西并非都是物件(objects)。理解reference type和primitive types之间的差异,及它们在JVM中的表述(representation),会使你在运用它们时得以做出明智的选择。

实践9:区分 == 和equals()

== 用来测试基本型别的相等性,亦可判定两个object references是否指向同一个object。若要测试values(值)或semantic(语意)相等,应使用equals()。

实践10:不要依赖equals()的预设实作(default implementation)

不要不假思索地认定一个class总是会正确实作出equals()。此外,java.lang.Object提供的equals()大多数时候并非进行你想要的比较。

实践11:实作equals()时必须深思熟虑

如果某个class的两个objects「即使不占用相同的记忆体空间,也被视为逻辑上相等」,那麽就该为这个class提供一个equals()。

实践12:实作equals()时优先考虑使用getClass()

实作equals()时请优先考虑采用getClass()。毕竟,「相同class下的objects才得被视为相等」是正确实作equals()的一个清晰简明的解决方案。

实践13:呼叫base class(基础类别)的super.equals()

任何base class(除了java.lang.Object)如果实作equals(),其derived class都应该呼叫super.equals()。

实践14:在equals()函式中谨慎使用instanceof

唯有当你考虑允许「一个derived class object可以相等於其base class object」时,才在equals()中使用instanceof。使用这项技术前请先弄清楚其影响。

实践15:实作equals()时需遵循某些规则

撰写equals()并非那麽直观浅白。如果想要恰当地实作出equals(),请遵循某些规则。

异常处理(Exception Handling)

实践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)

不要忽视记忆体以外的资源。垃圾回收机制不会替你释放它们。请使用finally确保记忆体以外的资源被释放。

实践22:不要从try区块中回返

不要从try区块中发出return指令,因为这个函式未必会立即从那儿回返。如果存在finally区段,它就会被执行起来并可能改变回传值。

实践23:将try/catch区块置於回圈(loop)之外

撰写含有异常处理的回圈时,请将try和catch区块置於回圈外部。在某些实作版本上,这会产生更快的执行码。

实践24:不要将异常(exceptions)用於流程控制

请将异常用於预期行为之外的情况。不要以异常来控制流程,请采用标准的语言流程构件(flow constructs),这样的流程表达会更清晰更高效。

实践25:不要每逢出错就使用异常(exceptions)

只有面对程式行为可能出乎预料的情境下才使用异常。「预期中的行为」应使用回传码(return codes)来处理。

实践26:在建构式(constructors)中抛出异常

尽管建构式并非函式(method),因而不能回传一个值,但建构式有可能失败。如果它们失败了,请抛出一个异常。

实践27:抛出异常(exceptions)之前先将object恢复为有效状态

抛出异常很容易,困难的是「将异常所引发的伤害减到最小」。抛出异常之前,应确保「如果异常被处理好,流程再次进入抛出异常的那个函式中,该函式可以成功完成」。

效能/效率(Performance)

实践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)

如果某个成本高贵的计算并非一定必要,就尽量少做。请使用「缓式评估」
(lazy evaluation,延迟求值)技术避免那些永远不需要的工作。

实践44:手工优化(optimize)你的程式码

由於Java编译器在优化方面的作为甚少,为了生成最佳byte code,请手工优化你的原始码。

实践45:编译为原生码(native code)

编译为原生码,通常可以导致执行速度更快的程式码。但你却因此必须在各种不同的原生方案(native solution)中取舍。

多绪(Multithreading)

实践46:面对instance函式,synchronized锁定的是物件而非函式或程式码

关键字synchronized锁定的是物件,而非函式或程式码。一个函式或程式
区段被宣告为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)

一般情况下请不要同步化所有函式。同步化不仅造成程式缓慢,并且丧失了
并行(concurrency)的可能。请采用「单物件多锁」技术以允许更多并行。

实践50:取用共享变数时请使用synchronized或volatile

不可切割(原子化,atomic)操作并非意味「执行绪安全」。JVM实作品被允许在私有记忆体中保留变数的工作副本。这可能会产生陈旧数值(stale values)。为避免这个问题,请使用同步化机制或将变数宣告为volatile。

实践51:在单一操作(single operation)中锁定所有用到的objects

同步化某一函式,并不一定就会使其成为「执行绪安全」的函式码。如果synchronized函式操控着多个objects,而它们并不都是此函式所属class的private instance data,那麽你必须对这些objects自身也进行同步化。

实践52:以固定而全域性的顺序取得多个locks(机锁)以避免死结(deadlock)

当你同步化多个物件,请以固定和全域性的顺序获得locks,以避免死结。

实践53:优先使用notifyAll()而非notify()

notify()只唤醒一个执行绪。要想唤醒多个执行绪,请使用notifyAll()。

实践54:针对wait()和notifyAll()使用旋锁(spin locks)

当你等待条件变数(condition variables)时,请总是使用旋锁(spin locks)以确保正确结果。

实践55:使用wait()和notifyAll()取代轮询回圈(polling loops)

将所有polling loops替换为使用wait()、notify()和notifyAll()的spin locks(旋锁)。使用spin locks直观而高效,使用polling loops则慢很多倍。

实践56:不要对locked object(上锁物件)之object reference重新赋值

当一个object被锁定,有可能其他执行绪会因同一个object lock而受阻(blocked)。假如你对上锁物件的object reference重新赋值,其他执行绪中悬而未决的那些locks将不再有意义。

实践57:不要呼叫stop()或suspend()

不要呼叫stop()或suspend(),因为它们可能导致资料内部混乱,甚至引发死结(deadlock)。

实践58:透过执行绪(threads)之间的合作来中止执行绪

你不应该呼叫stop()。如欲安全地停止执行绪,必须要求它们相互协作,才能姿态优雅地中止。

类别与介面(Classes and Interfaces)

实践59:使用interface支援多重继承(multiple inheritance)

当你想要支援interface的单一继承或多重继承,或者想要实作一个标记式的(marker)interface时,请使用interfaces。

实践60:避免interfaces中的函式发生冲突

没有任何办法能够阻止两个interfaces使用同名的常数和函式。为了避免可能的冲突,应当小心命名常数和函式。

实践61:需要提供部分实作(partial implementation)时,请使用abstract classes

使用abstract class来为一个class提供部分实作,这些实作很可能对derived class是共通的(都被需要的)。

实践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覆写,在建构式中呼叫这个函式可能会导致不可预期的结果。


前言
让无知尽管信口开河吧,学习自有其价值
Let ignorance talk as it will, learning has its value.
J. de La Fontaine, The Use of Knowledge, Book viii, Fable 19

本书汇集了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错误观念,并强调语言特性方面的某些问题。

本书组织

本书组织为六大部分。

一般技术 展现Java编程的数个基础领域,包括引数传递(parameter passing),arrays,Vectors以及垃圾回收(garbage collection)。
物件与相等性 研究物件(objects)和基础型别(primitive types),以及如何、为何为一个class实作equals()。

异常处理 提供异常处理技术(exception handling techniques)的细致分析,并告诉你如何在你的程式码中高效加入异常处理机制。
效能(效率) 展示可用来改善程式码效能的诸多技术,并仔细审查JVM(Java虚拟机器)、byte code和JITs(及时程式码生成器)。
多执行绪 涵盖执行绪模型(threading model)的诸多方面 它们对於建立健壮、可靠的多执行绪程式极为关键。
类别与介面 解释了interfaces、abstract classes和concrete classes,以及何处、何时使用它们。这部分还详细讨论了immutable object(不可变物件)、cloning(克隆、复制)和finalization(终结)。
在上述各标题之下,是数量不等的相关专题。往往我会在不止一处讨论特定专题的某个属性。例如我在不同场合讨论了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

Android应用开发提高系列(1)——《Practical Java 中文版》读书笔记(上)

书籍   《Practical Java 中文版》  03版  侯捷、刘永丹译     本书和《Effective Java》,对前述重要而基础的技术细微处有着详尽、深刻、实用的介绍、剖析和范例,...

Practical Java(重点版)之异常处理

以下是学习《Practical Java(重点版)》的笔记。 1.  认识异常控制流(exception control flow)机制。当异常发生时,会转入到以下三种的处理方式之一: a).ca...

Practical Java(重点版)之多线程

1. 面对instance 函数,synchronized 锁定的是对象(objects)而非函数(methods)或代码(code)。    Synchronized既可以作用于方法修饰,也可以用...

Practical Java(重点版)之一般技术

以下是学习《Practical Java(重点版)》的笔记。 1.   java的方法参数传的都是值,而不是引用,传的对象其实是地址的值。所谓的java传引用是错误的说法,在C#中才有传引用的说法。...

Android应用开发提高系列(2)——《Practical Java 中文版》读书笔记(下)

正文   注意:条目和用语可能与书籍有所出入,但尽量保持原样加一些自己的理解。   一、性能     1. 先把焦点放在设计、数据结构和算法身上       备注:良好的设计、明智的选择...

【Practical Java】实践10:不要依赖equals()的缺省实现

实践9 阐释了何时使用==操作符以及何时使用equals()。如果你对后者的实现方式不闻不问,则在调用它时或许无法获得你想要的结果。举个例子,假设你正在为某个高尔夫器材批发店撰写软件,其中一个任务是计...

Practical Java(重点版)之对象(完)

1. 运用interfaces 支持多重继承(multiple inheritance)。 Java的接口是支持多继承的。Java是不支持实现上的多继承。 2. 避免interfaces 中的函数...

关于高效能java(practical java学习笔记)

写在最前面,本文中的所有内容根据practical java一书整理而成,仅作为个人笔记,更多详情请参详书中内容,请支持正版。ps:这是一本绝对的好书,非常值得很多次的看!!!!     缺省情...

Practical Java中文版电子书

  • 2009年06月23日 22:26
  • 4.78MB
  • 下载

Practical Java

  • 2008年11月01日 12:10
  • 3.74MB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Practical Java
举报原因:
原因补充:

(最多只允许输入30个字)