一、介绍
为什么需要代码规范 - 由于以下几个原因,使得代码规范对开发者来说显得重要:
1) 80%的项目生命期处于维护阶段。@b@2) 几乎所有的软件都不是由最初的作者在进行维护。 @b@3) 代码规范改善了软件可读性,允许更快、更彻底的理解新代码。 @b@4) 如果在产品中使用了您的源代码,您需要确信代码对产品来说是封装良好的。
二、文件命名
本节是列出了文件扩展名和文件命名的一般性习惯。
2.1 文件扩展名 - JAVA软件使用以下文件扩展名:
文件类型 | 扩展名 |
Java源程序 |
|
Java字节码 |
|
2.2 文件命名 - 经常使用的文件名称有:
文件名 | 用途 |
| Makefiles首选名称. 我们使用 |
| Readme文件首选名称。 |
2.3 文件组织 - 组成文件的段落应当用空行分开,并且可以使用注释将每个段落标识出来。 超过2000行的文件会显示臃肿,因此应当尽量避免。
三、JAVA源文件
每一个Java源文件包含一个公共的类或者接口。当私有的类和接口与这个公共的类和接口关联时,您可以将它们放到相同的文件中。公共类和接口应当是文件中的第一个类或者接口。在下面几个访问存在注释
1)文件开始处的注释 (参见 "开始注释") @b@2)包和导入定义@b@3)类和接口定义 (参见 "类和接口定义")
3.1 开始注释 - 所有的源文件都应该以一个C风格的注释开始,注释中应当列出类名,版本信息,日期和版权声明:
/*@b@ * 类名@b@ * @b@ * 版本信息@b@ *@b@ * 日期@b@ * @b@ * 版权声明@b@ */
3.2 包和导入声明 - 绝大多数JAVA源文件的第一个非注释行是package语句。之后, import 语句出现。如:
package java.awt;@b@import java.awt.peer.CanvasPeer;@b@@b@//提示: 唯一包名的第一部分总是以全小写ASCII 字符书写,并且应当是顶级域名,通常是com, edu, gov, mil, net, org或者两位由@b@//英文字母标识的国家代码。国家代码由ISO Standard 3166, 1981规范指定(译者注:如中国的国家代码为cn)。
3.3 类和接口定义 - 下表描述了接口和类定义,定义的样式请参见"Java代码示例".
类和接口定义 | 备注 | |
1 | 类/接口文档注释 ( | 参见 "文档注释". |
2 |
| |
3 | 如果有必要,对类/接口编写实现注释 ( | 该注释应当包含任何接口/类信息,这些信息不适合在类/接口文档注释中出现。 |
4 | 类 ( | 首先是公共类变量,其次是保护变量,其次是包级别变量 (无访问修饰符), 最后是私有变量. |
5 | 实例变量 | 首先是公共实例变量,其次是保护变量,其次是包级别变量 (无访问修饰符), 最后是私有变量. |
6 | 构造器 | |
7 | 方法 | 以功能对这些方法分组比以作用域或者可访问性分组更适当. 例如, 一个私有的类方法可以在公共的实例方法之间. 其目的是使阅读和理解代码更容易. |
四、缩排
缩排应当以四个空格为单位. 没有强制的缩排规定 (空格vs. tabs)(译者注:这是java语言规范中定义的,在大多数的项目中,均规定以四个空格为缩排单位而不是tabs)。Tabs必须被设置为八个空格 (而不是四个).
4.1 行宽 - 避免每行超过80字符,因为超过部分不会被许多终端或者工具处理。 注意: 文档中使用的例子应当更短,一般不要超过70个字符.
4.2 折行 - 当一个表达式不能在一行中书写完毕时,通过以下方式折行:
在“,”后面折行. @b@在一个操作符前折行. @b@以代码层次高低决定折行,层次高的代码在同一行. @b@同一层次的代码左边对齐. @b@如果上一规则使代码变得不清晰,则统一都缩排8个空格.
以下是一些方法调用折行的示例:
someMethod(longExpression1, longExpression2, longExpression3, @b@ longExpression4, longExpression5);@b@ @b@var = someMethod1(longExpression1,@b@ someMethod2(longExpression2,@b@ longExpression3));
以下是两个算术表达式折行的例子. 第一个例子是推荐的, 因为折行发生在()外面。
longName1 = longName2 * (longName3 + longName4 - longName5)@b@ + 4 * longname6; // 更好的@b@ @b@longName1 = longName2 * (longName3 + longName4@b@ - longName5) + 4 * longname6; // 应当避免的
以下是两个过程定义的折行例子。第一个是常规的选择.第二个例子应该将第二行和第三行右移, 一般的,应当以8个空格进行缩排.
//常规的缩排@b@someMethod(int anArg, Object anotherArg, String yetAnotherArg,@b@ Object andStillAnother) {@b@ ...@b@}@b@ @b@//缩排8个空格,以避免太深的缩排@b@private static synchronized horkingLongMethodName(int anArg,@b@ Object anotherArg, String yetAnotherArg,@b@ Object andStillAnother) {@b@ ...@b@}
IF语句通常应当以8个空格进行缩排,因为4空格(常规的)会使语句体看起来困难,如:
//不要使用这种方式@b@if ((condition1 && condition2)@b@ || (condition3 && condition4)@b@ ||!(condition5 && condition6)) { //BAD WRAPS@b@ doSomethingAboutIt(); //MAKE THIS LINE EASY TO MISS@b@} @b@ @b@//应当换成这种方式@b@if ((condition1 && condition2)@b@ || (condition3 && condition4)@b@ ||!(condition5 && condition6)) {@b@ doSomethingAboutIt();@b@} @b@ @b@//或者换成这种方式@b@if ((condition1 && condition2) || (condition3 && condition4)@b@ ||!(condition5 && condition6)) {@b@ doSomethingAboutIt();@b@}
这是三种可以接受的三目条件表达式:
alpha = (aLongBooleanExpression) ? beta:gamma; @b@alpha = (aLongBooleanExpression) ? beta:gamma; @b@alpha = (aLongBooleanExpression) ? beta:gamma;
五、注释
Java程序有两种注释: 实现注释和文档注释. 实现注释在C++中就存在了, 它们以 /*...*/, 和 //. 为分隔符。文档注释为JAVA独有, 并且以 /**...*/.为分隔符。- 文档注释可以被javadoc工具提取出HTML文档。实现注释表示对代码块的注释或者对代码实现细节的注释。文档注释是对代码规格进行描述。
5.1 实现注释的格式 - 程序包含四种风格的实现注释: 块注释, 单行注释, 跟随注释, 和行注释.
5.1.1 块注释
块注释用于提供文件、方法、数据结构、算法的描述。块注释可以在每个文件开始处或者每个方法之前使用。也可以在其他地方,如方法内使用。方法内部的块注释应当为与代码排列整齐。块注释应当以一个空行开头,以使其与其他代码区分开来,以/*-开始的块注释不会被重新格式化。如:
/*-@b@ * Here is a block comment with some very special@b@ * formatting that I want indent(1) to ignore.@b@ *@b@ * one@b@ * two@b@ * three@b@ */
5.1.2 单行注释
短的注释可以用与代码并排的单行注释来实现。如果注释不能在一行中书写,则应当使用多行注释。 单行注释应当以空行开始. 以下是一个单行注释的例子:
if (condition) {@b@@b@ /* Handle the condition. */@b@ ...@b@}
5.1.3 跟随注释
非常短的注释可以与代码同行,但是应当与代码隔得足够远。如果多个跟随注释与同一个代码块相关, 它们应当排列整齐. 这是一个跟随注释的例子:
if (a == 2) {@b@ return TRUE; /* special case */@b@} else {@b@ return isPrime(a); /* works only for odd a */@b@}
5.1.4 行注释
// 注释分隔符可以注释一个完整的行或者一行的某个部分.它不应当用来对多行代码进行代码注释;无论如何,它可以用来注释掉多行代码。以下是三类型的注释例子:
if (foo > 1) {@b@ @b@ // Do a double-flip.@b@ ...@b@}@b@else {@b@ return false; // Explain why here.@b@}@b@//if (bar > 1) {@b@//@b@// // Do a triple-flip.@b@// ...@b@//}@b@//else {@b@// return false;@b@//}
6 – 定义
6.1 每行的数量
// 推荐每行只定义一个变量,这样便于书写注释。 @b@int level; // indentation level@b@int size; // size of table@b@//上面的书写方面是首选的。 @b@int level, size;@b@//不要在同一行书写不同类型的定义,如: @b@int foo, fooarray[]; //错误的!@b@//注意: 上例中,类型和实例间使用了一个空格。另一种可接受的的做法是使用tabs: @b@int level; // indentation level@b@int size; // size of table@b@Object currentEntry; // currently selected table entry
6.2 初始化 - 尽量在定义时对变量进行初始化。其中一个原因是:如果不在定义时初始化,其他变量在初始化时可能会依赖该变量。
6.3 位置 - 仅仅在块开始的地方放置定义。. (块是指由 "{" 和 "}"围绕的代码.) 不要在变量使用时才定义; 它会使粗心的程序员搞糊涂.
void myMethod() {@b@ int int1 = 0; // 在方法块的开头定义@b@ @b@ if (condition) {@b@ int int2 = 0; // 在"if" 块的开头定义。@b@ ...@b@ }@b@}@b@ // 一个例外的情况是:在JAVA中,FOR循环的索引变量, 可以在FOR语句中定义:@b@for (int i = 0; i < maxLoops; i++) { ... }@b@ // 应当避免局部变量隐藏更高作用域的变量。. 如,不要在块里面定义与外层块中同名的变量:@b@int count;@b@...@b@myMethod() {@b@ if (condition) {@b@ int count = 0; // 应当避免!@b@ ...@b@ }@b@ ...@b@}
6.4 类和接口语句 - 在编写类和接口时, 应当沿用以下的格式规则:
1) 在方法名和 "("之间不要有空格。这个"("用于开始参数列表。@b@2) "{" 与定义语句在同一行,并在该行末尾@b@3) }" 单独处于一行,并与定义行左对齐。除非过程体为空,此时"}"应当紧跟在"{"后面,并与"{"处于同一行
class Sample extends Object {@b@ int ivar1;@b@ int ivar2;@b@ @b@ Sample(int i, int j) {@b@ ivar1 = i;@b@ ivar2 = j;@b@ }@b@ @b@ int emptyMethod() {}@b@ @b@ ...@b@}@b@ 方法之间以空行分开。
7 – 语句
1 简单语句@b@每行最多包含一条语句. 如:@b@argv++; // 正确@b@argc--; // 正确 @b@argv++; argc--; // 应当避免!@b@2 复合语句@b@复合语句是指由"{ statements }"包含的语句列表。.@b@“}”应当在块内的语句基础上向左缩排一个层次. @b@“{“应当在复合语句开始行的末尾,(译者注:如在if语句的末尾); “}”应当折行。.@b@“{”,“}”用于包围住块内所有代码,即使块内只有一个单行,也需要用“{”,“}”包围起来。这样可以更容易添加块内语句,避免因为在添加块内语句时忘记加上“{”,“}”而意外的引入BUG@b@3 返回语句@b@有返回值的返回语句不要使用“()”,除非一些显而易见的原因需要加上(). 如:@b@return;@b@ @b@return myDisk.size();@b@ @b@return (size ? size : defaultSize);@b@ @b@4 if, if-else, if else-if else 语句@b@if-else 语句应当如以下格式:@b@if (condition) {@b@ statements;@b@}@b@ @b@if (condition) {@b@ statements;@b@} else {@b@ statements;@b@}@b@ @b@if (condition) {@b@ statements;@b@} else if (condition) {@b@ statements;@b@} else {@b@ statements;@b@}@b@ @b@ 注意: if 语句总是应该使用{}.应当避免以下错误格式:@b@if (condition) //错误,缺少{}!@b@ statement;@b@5 for 语句@b@for应当以以下格式书写:@b@for (initialization; condition; update) {@b@ statements;@b@}@b@ 空的FOR语句(所有工作在FOR语句的初始,条件判断,修改部分完成)应当是以下格式:@b@for (initialization; condition; update);@b@ 在FOR语句中,如果在初始子句、修改子句中使用了“,”操作符, 应当避免使用三个以上的变量. 如果确实有必要,可以在FOR循环前或者循环体结束时使用多条语句.@b@6 while语句@b@while语句应当使用以下格式:@b@while (condition) {@b@ statements;@b@}@b@ 空while语句应当使用以下格式:@b@while (condition);@b@7 do-while 语句@b@do-while语句应当使用以下格式:@b@do {@b@ statements;@b@} while (condition);@b@8 switch语句@b@switch语句应当使用以下格式:@b@switch (condition) {@b@case ABC:@b@ statements;@b@ /* falls through */@b@ @b@case DEF:@b@ statements;@b@ break;@b@ @b@case XYZ:@b@ statements;@b@ break;@b@ @b@default:@b@ statements;@b@ break;@b@}@b@ 没有break语句时,会接着执行下一个case语句块,应当在没有break的case块中加入一个注释.@b@每个switch 应当包含一个default分支. 虽然此时break语句是多余的, 但是当以后增加一个case分支时,它可以防止执行新加的代码(译者注:所以在default分支中加入break是必需的).@b@9 try-catch 语句@b@try-catch语句应当使用以下格式:@b@try {@b@ statements;@b@} catch (ExceptionClass e) {@b@ statements;@b@}@b@ try-catch也可以跟随finally子句.@b@try {@b@ statements;@b@} catch (ExceptionClass e) {@b@ statements;@b@} finally {@b@ statements;@b@}
8 – 空白符
1. 空行@b@通过空行,将逻辑相关的代码分块,可以改善程序的可读性.@b@以下情况,需要用两个空行:@b@一个源文件中的两个段落中。@b@在两个类和接口定义之间@b@以下情况,需要用一个空行:@b@方法之间@b@方法的局部变量和第一条语句之间@b@开始一个代码块或者单行注释时@b@在一个方法内改善可读性时(通过空行使逻辑相关的代码块组织在一起)@b@2 空格@b@在以下情况中,需要使用空格:@b@关键字与其后的“(”之间. 如:@b@ while (true) {@b@ ...@b@ }@b@ @b@ 注意:在方法名和其后的“(”之间不应该有空格。.@b@在参数列表的“,”后面应该有一个空格@b@所有的二元操作符,应当与其操作数据之间有空格,所有一元操作符与其操作数之间不应当有空格符, 如("++"), ("--")。如:@b@ a += c + d;@b@ a = (a + b) / (c * d);@b@ @b@ while (d++ = s++) {@b@ n++;@b@ }@b@ printSize("size is " + foo + "\n");@b@ @b@ For语句的表达式之间. 如:@b@ for (expr1; expr2; expr3)@b@ @b@ 类型转换时,应当在前面加一空格. 如:@b@ myMethod((byte) aNum, (Object) x);@b@ myMethod((int) (cp + 5), ((int) (i + 3)) @b@ + 1);
9 – 命名规范 - 命名规范通过使程序易读,从而使用程序更易懂。
标识符类型 | 命名规则 | 例子 |
包 | 包名前缀总是以全小写的ASCII 字符书写,并且应当是一个顶级域名。如com, edu, gov, mil, net, org, 或者两个字符的国家代码,参见ISO Standard 3166, 1981. 随后的部分根据组织的国际域名命名. 也可以是公司, 部门, 项目, 机构, 或者登录名. | com.sun.eng com.apple.quicktime.v2 edu.cmu.cs.bovik.cheese |
类 | 类名应当是名词, 每个单词首字母大写. 应当保持类名简单,有意义. 使用全名,避免单词缩写(除非缩写是约定成俗并且比全称更普遍。如 URL、HTML). | class Raster; class ImageSprite; |
接口 | 接口应当象类名那样使用首字母大写. | interface RasterDelegate; interface Storing; |
方法 | 方法应当是动词, 首个单词的首字母小写, 随后的单词首字母大写. | run(); runFast(); getBackground(); |
变量 | 变量的首个单词首字母都小写. 其他单词首字母大写. 变量名不以“-”和“$”开始. 变量名应当短而有意义. 变量也应当好记。. 除非是即用即弃的临时变量,不要使用单字符的变量. 一般的即用即弃临时整形变量可以是 | int i; char c; float myWidth; |
常量 | 所有类常量和ANSI常量应当全部使用大写字母,单词之间以 ("_")分开. (为调试方便,应当使用ANSI常量.) | static final int MIN_WIDTH = 4; static final int MAX_WIDTH = 999; static final int GET_THE_CPU = 1; |
10 – 程序惯例
1 访问实例和类变量@b@除非有充分的理由,不要使用公共的类变量、实例变量. 通常,实例变量不需要显式的设置和读取。需要这样做的时候,使用方法调用.@b@一个合适的使用公共实例变量的例子是:当类仅当成数据结构使用,而没有行为(译者注:没有方法调用)。@b@2 类变量和方法@b@避免通过类实例访问类方法和类变量. 应该使用类名代替. 如:@b@classMethod(); //OK@b@AClass.classMethod(); //OK@b@anObject.classMethod(); //避免!@b@3 常量@b@数字常量应该直接在代码中编写, 除非象-1, 0, 和 1这种可以在for循环中作为计数器使用的数字.@b@4 变量赋值@b@避免在同一行中将一个值赋给多个变量. 这使代码阅读更困难,如:@b@fooBar.fChar = barFoo.lchar = 'c'; // 应当避免!@b@ 下面的写法不被接受:@b@if (c++ = d++) { // 应当避免! (Java 不接受)@b@ ...@b@}@b@ 应当这样写@b@if ((c++ = d++) != 0) {@b@ ...@b@}@b@ 不要为了提高性能而使用内嵌的赋值语句. 那是编译器的任务. 如:@b@d = (a = b + c) + r; // 应当避免!@b@ 应当这样写@b@a = b + c;@b@d = a + r;@b@5 其他惯例@b@5.1 括号@b@使用括号来改变表达式中的运算次序是不错的主意. 有时也可以强制加上()以使代码更好理解,尽管在某些情况下不加()的结果正确的.@b@if (a == b && c == d) // 应当避免!@b@if ((a == b) && (c == d)) // 正确@b@5.2 返回值@b@如:@b@if (booleanExpression) {@b@ return true;@b@} else {@b@ return false;@b@}@b@ 应当被写成@b@return booleanExpression;@b@ 同样的,@b@if (condition) {@b@ return x;@b@}@b@return y;@b@ 应当被写成@b@return (condition ? x : y);@b@5.3 ?前的条件表达式@b@如果条件表示式包含二元操作符,应当用()包围起来. 如:@b@(x >= 0) ? x : -x;@b@5.4 特殊注释@b@在注释中使用XXX表示假的,但是可能会存在的事物. 用 FIXME 假的并且被确定不存在的事物.
11 – 代码示例
11.1 JAVA代码示例 - 以下是一个完事的java源文件例子:
/*@b@ * @(#)Blah.java 1.82 99/03/18@b@ *@b@ * Copyright (c) 1994-1999 Sun Microsystems, Inc.@b@ * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.@b@ * All rights reserved.@b@ *@b@ * This software is the confidential and proprietary information of Sun@b@ * Microsystems, Inc. ("Confidential Information"). You shall not@b@ * disclose such Confidential Information and shall use it only in@b@ * accordance with the terms of the license agreement you entered into@b@ * with Sun.@b@ */@b@ @b@ @b@package java.blah;@b@ @b@import java.blah.blahdy.BlahBlah;@b@ @b@/**@b@ * Class description goes here.@b@ *@b@ * @version 1.82 18 Mar 1999@b@ * @author Firstname Lastname@b@ */@b@public class Blah extends SomeClass {@b@ /* A class implementation comment can go here. */@b@ @b@ /** classVar1 documentation comment */@b@ public static int classVar1;@b@ @b@ /** @b@ * classVar2 documentation comment that happens to be@b@ * more than one line long@b@ */@b@ private static Object classVar2;@b@ @b@ /** instanceVar1 documentation comment */@b@ public Object instanceVar1;@b@ @b@ /** instanceVar2 documentation comment */@b@ protected int instanceVar2;@b@ @b@ /** instanceVar3 documentation comment */@b@ private Object[] instanceVar3;@b@ @b@ /** @b@ * ...constructor Blah documentation comment...@b@ */@b@ public Blah() {@b@ // ...implementation goes here...@b@ }@b@ @b@ /**@b@ * ...method doSomething documentation comment...@b@ */@b@ public void doSomething() {@b@ // ...implementation goes here... @b@ }@b@ @b@ /**@b@ * ...method doSomethingElse documentation comment...@b@ * @param someParam description@b@ */@b@ public void doSomethingElse(Object someParam) {@b@ // ...implementation goes here... @b@ }@b@}