贲文提供一整套编写高效可靠的
Java
代码的标准、约定和指南。它们以安全可靠的软件工程原则为基础,使代码易于理解、维护和增强。而且,通过遵循这些程序设计标准,你作为一个
Java
软件开发者的生产效率会有显著提高。经验证明,若从一开始就花时间编写高质量的代码,则在软件开发阶段,对代码的修改要容易很多。最后,遵循一套通用的程序设计标准将带来更大的一致性,使软件开发团队的效率明显提高。
运用常识。
当找不到任何规则或指导方针,当规则明显不能适用,当所有的方法都失效的时侯:
运用常识并核实这些基本原则。这条规则比其它所有规则都重要。常识是必不可少的。
Java
的程序设计标准很重要,原因在于它将提高开发团队各成员的代码的一致性。一致性的提高会使代码更易理解,这意味着它更易开发和维护。从而降低了应用程序的总开发成本。你必须牢记的是:你的
Java
代码在你已离开并开始另一个项目之后,会保留相当长的一端时间。因此开发过程中一个很重要的目标就是要确保在开发成员或开发团队之间的工作可以顺利交接,不必花很大的力气便能理解已编写的代码,以便继续维护和改进以前的工作。如果代码难以理解,很有可能被废弃和重写。
我们将在整个标准中讨论命名约定,所以让我们先讨论几个基本点:
1.
使用可以准确说明变量
/
字段
/
类的完整的英文描述符。
例如,采
用类似
firstName
,
grandTotal
或
CorporateCustomer
这样的名字。虽然象
x1
,
y1
或
fn
这样的名字很简短,输入起来容易,但是我们难以知道它们代表什么、结果是什么含义,因而使代码难以理解、维护和改进。
2.
采用该领域的术语。
如果用户称他们的
“
客户
” (clients)
为
“
顾客
” (customers)
,那么就采用术语
Customer
来命名这个类,而不用
Client
。许多程序开发者会犯的一个错误是,不去使用工业或领域里已经存在着很完美的术语时,却生造出一些普通词汇。
3.
采用大小写混合,提高名字的可读性。
一般应该采用小写字母,但是类和接口的名字的首字母,以及任何中间单词的首字母应该大写
[KAN97]
。
4.
尽量少用缩写,但如果一定要使用,就要谨慎地使用。
这意味着应该保留一个标准缩写的列表,明智地从中选取,并且在使用时保持一致。例如,想对单词
“number”
采用缩写,那么可从
nbr
,
no
或者
num
中选取一个,说明一下采用了哪一个(具体是哪个倒无所谓),并且只使用这一种形式。
5.
避免使用长名字(最好不超过
15
个字母)。
虽然
PhysicalOrVirtualProductOrService
看起来似乎是个不错的类名,但是这个名字太长了,应该考虑重新给它起个短一点的名字,比如象
Offering
。
6.
避免使用相似或者仅在大小写上有区别的名字。
例如:
intx,X; //
变量x 与 X
容易混淆
void foo(int x); //
函数foo 与FOO
容易混淆
void FOO(float x);
7.
避免使用下划线作为名字的首末字母。
以下划线为首末字母的名字通常为系统保留,除预处理定义之外,一般不用作用户命名。更重要的是,下划线经常造成麻烦而且难输入,所以尽量避免使用。
8.
命名规则尽量与所采用的操作系统或开发工具的风格保持一致。
例如Windows应用程序的标识符通常采用“大小写”混排的方式,如AddChild。而Unix应用程序的标识符通常采用“小写加下划线”的方式,如add_child。别把这两类风格混在一起用。
9.
程序中不要出现标识符
完全相同的局部变量和全局变量
。尽管两者的作用域不同而不会发生语法错误,但会使人误解
- 变量的名字应当使用“名词”或者“形容词+名词”。
例如:
floatvalue;
floatoldValue;
floatnewValue;
11.
全局函数的名字应当使用“动词”或者“动词+名词”(动宾词组)。类的成员
函数应当只使用“动词”,被省略掉的名词就是对象本身。
例如:
DrawBox(); //
全局函数
Box.Draw(); //
类的成员函数
12.
用正确的反义词组命名具有互斥意义的变量或相作的函数等。
例如:
int minValue;
int maxValue;
int SetValue(…);
int GetValue(…);
13.
尽量避免名字中出现数字编号,如Value1,Value2等,除非逻辑上的确需要编号。
这是为了防止程序员偷懒,不肯为命名动脑筋而导致产生无意义的名字(因为用数字编号最省事)。
14.
类名和函数名用大写字母开头的单词组合而成。
例如:
class Node; //
类名
class LeafNode; //
类名
voidDraw(void); //
函数名
voidSetValue(int value);//
函数名
15.
变量和参数用小写字母开头的单词组合而成。
例如:
boolean flag;
intdrawMode;
16.
常量全用大写的字母,用下划线分割单词。
例如:
int MAX = 100;
int MAX_LENGTH = 100;
17.
静态变量加前缀
s_
(表示
static
)。
例如:
void Init(…)
{
static int s_initValue; //
静态变量
…
}
18.
如果不得已需要全局变量,则使全局变量加前缀
g_
(表示
global
)。
例如:
int g_howManyPeople;//
全局变量
int g_howMuchMoney;//
全局变量
19.
类的数据成员加前缀
m_
(表示
member
),
这样可以避免数据成员与成员函数的参数同名。
例如:
void SetValue(int width, int height)
{
m_width = width;
m_height = height;
}
我们还会对注释约定进行讨论,所以,我们先谈谈一些基本点:
1.
注释应该增加代码的清晰度。
代码注释的目的是要使代码更易于被同时参与程序设计的开发人员以及其他后继开发人员理解。
2.
如果你的程序不值得注释,那么它也很可能也不值得运行
[NAG95]
。
3.
避免使用装饰性内容,也就是说,不要使用象广告横幅那样的注释语句。
二十世纪六十年代和七十年代,
COBOL
程序员们养成了画框的习惯,典型的是用星号将他们的内部注释圈起来。当然,这给他们的艺术创造欲一个发泄方式,但是坦白地说,这只是在大量浪费时间,并不能给最终的产品增加丝毫价值。要写的是清晰的代码,不是外表可爱的代码。此外,由于有些字体的显示和打印是成比例的,但有些又不是,所以无法将那些框排整齐。
4.
保持注释的简洁。
最好的注释应该是简单明了的注释。注释不必洋洋洒洒,只需提供足够的信息,使别人能够理解你的代码。
5.
先写注释,后写代码。
写代码注释的最好方法是在写代码之前就写注释。这使你在写代码之前可以想想代码的功能和运行。而且这样确保不会遗漏注释。另一种方法是边写代码边写注释。因为注释可以使代码更易理解,所以在程序开发的过程中,也可以利用这一点。如果打算花些时间写注释,那么至少你应从这个过程中获得些什么
[AMB98]
。
6.
注释信息不仅要包括代码的功能,还应给出原因。
例如,下面例
1
中的代码显示金额在
$1,000
以上(包括
$1,000
)的定单可给予
5%
的折扣。为什么要这样做呢?难道有一个商业法则规定大额定单可以得到折扣吗?这种给大额定单的特殊是有时限的呢,还是一直都这样?最初的程序设计者是否只是由于慷慨大度才这样做呢?除非它们在某个地方(或者是在源代码本身,或者是在一个外部文档里)被注释出来,否则你不可能知道这些。
例 1.1
if (grandTotal >= 1000.00)
{
grandTotal = grandTotal * 0.95;
}
2.2.1.Java 注释语句类型
Java
有三种注释语句风格:以
/**
开始,
*/
结束的文档注释,以
/*
开始,以
*/
结束的
C
语言风格注释,以及以
//
开始,代码行末尾结束的单行注释。下表是对各类注释语句建议用法的一个概括,也给出了几个例子。
注释语句类型
|
用法
|
示例
|
文档注释
|
在接口、类、成员函数和字段声明之前紧靠它们的位置用文档注释进行说明。文档注释由
javadoc
处理,为一个类生成外部注释文档,如下所示。
|
/**
Customer
(顾客)顾客是指作为我们的服务及产品的销售对象的任何个人或组织。
@author S.W. Ambler
*/
|
C
语言风格注释
|
采用
C
语言风格的注释语句将无用的代码注释掉。保留这些代码是因为用户可能改变想法,或者只是想在调试中暂时不执行这些代码。
|
/*
这部分代码已被它前面的代码替代,所以于 1999 年6 月 4 日被 B. Gustafsson 注释掉。如果两年之后仍未用这些代码,将其删除。
. . .
(源代码)
*/
|
单行注释
|
在成员函数内部采用单行注释语句对业务逻辑、代码片段和临时变量声明进行说明。
|
//
因为让利活动
//
从 1995 年 2 月开始,
//
所以给所有超过 $1000 的
//
发货单 5% 的折扣。
|
一件很重要的事情是,你的机构应该制订一套如何使用
C
语言风格注释和单行注释的标准,并始终严格遵守。使用一种注释方式来说明业务逻辑,使用另一种方式注释掉旧的代码。业务逻辑采用单行注释,因为这样可以将注释和代码放在同一行(这又叫做
“
内联
”
)。采用
C
语言风格的注释屏蔽掉旧的代码,因为这样可以同时注释掉数行。
C
语言风格注释看起来很象文档注释,所以为了防止混淆,不应在别处使用。
注意行末注释。
[MCO93]
强烈反对采用行内注释,即在一行的末尾加上注释。他指出,这种注释必须在代码的右端对齐,这样才能避免代码结构看起来混乱。结果,这些注释的格式难以划一。
“
如果你使用了很多这样的注释,则要花时间去将它们排列整齐。这些时间并没有花在更多地了解代码上,而完全花在了敲击空格键和制表符这种冗长乏味的工作上。
”
他又指出,行末注释也难以维护。因为当该行程序的代码加长时,它们会将这些注释挤出该行,如果你将它们排齐了,你不得不对余下的注释做同样的工作。
2.2.2.快速浏览javadoc
Sun
公司的
Java Development Kit (JDK)
中有一个名为
javadoc
的程序。它可以处理
Java
的源代码文件,并且为
Java
程序产生
HTML
文件形式的外部注释文档。
Javadoc
支持一定数目的标记,标识注释文档中各段起始位置的保留字。详情请参考
JDK javadoc
文档。
标记
|
用于
|
目的
|
@author name
|
类、
接口
|
说明特定某一段程序代码的作者。每一个作者各有一个标记。
|
@deprecated
|
类、
成员函数
|
说明该类的应用程序编程接口
(API)
已被废弃,因此应不再使用。
|
@exception name description
|
成员函数
|
说明由成员函数发出的异常。一个异常采用一个标记,并要给出异常的完整类名。
|
@param name description
|
成员函数
|
用来说明传递给一个成员函数的参数,其中包括参数的类型
/
类和用法。每个参数各有一个标记。
|
@return description
|
成员函数
|
若成员函数有返回值,对该返回值进行说明。应说明返回值的类型
/
类和可能的用途。
|
@since
|
类、成员函数
|
说明自从有
JDK 1.1
以来,该项已存在了多长时间。
|
@see ClassName
|
类、接口、成员函数、字段
|
在文档中生成指向特定类的超文本链接。可以并且应该采用完全合法的类名。
|
@see ClassName#member functionName
|
类、接口、成员函数、字段
|
在文档中生成指向特定成员函数的超文本链接。可以并且应该采用完全合法的类名。
|
@version text
|
类、接口
|
说明特定一段代码的版本信息。
|
你注释代码的方式很大地影响着你的工作效率以及所有维护改进代码的后继开发者的工作效率。在软件开发过程中及早注释代码,会促使你在开始撰写代码之前仔细考虑这些代码,从而带来更高的工作效率。而且,当你重新阅读数天前或者数星期前所写的代码时,你可以很容易地判断出当时你是怎么想的,因为这一切都有记录。
切记:你今天所写的代码可能在今后的数年里仍在使用,而且很有可能由其他人来维护和改进。应尽可能使你的代码
“
整洁
”
易懂,因为这会使代码易于维护和改进。
成员函数的命名应采用完整的英文描述符,大小写混合使用:所有中间单词的第一个字母大写。成员函数名称的第一个单词常常采用一个有强烈动作色彩的动词。
示例:
openAccount()
printMailingLabel()
save()
delete()
这种约定常常使人一看到成员函数的名称就能判断它的功能。虽然这种约定要使开发者多做一些输入的工作,因为函数名常常较长,但是回报是提高代码的可理解性。
在后续章节中,我们将更详细地讨论获取和存放字段值(字段
/
属性)的存取成员函数。下面将概括一下存取函数的命名约定。
获取函数作为一个成员函数,返回一个字段的值。除了布尔字段之外,应采用
get
作为字段的前缀;布尔字段采用
is
作为前缀。
示例:
getFirstName()
getAccountNumber()
isPersistent()
isAtEnd()
遵循这个命名约定,显然,成员函数将返回对象的字段,布尔型的获取函数将返回布尔值
“
真
”
或者
“
假
”
。这个标准的另一个优点是:它遵循
beans development kit (BDK)
对获取成员函数采用的命名约定
[DES97]
。它的一个主要的缺点是
get
是多余的,需要额外的录入工作。