1 Java编程规范
1.1 命名约定
1.1.1 包的命名
JDK 的标准是——包名称的所有字母均小写,如:
package myownpackage;
1.1.2 类的命名
JDK 的标准是——大写一个类名的首字母,若类名由几个单词构成,那么把它们紧靠到一起(也就是说,不要用下划线来分隔名字)。此外,每个嵌入单词的首字母都要采用大写形式。类名称应该是一个名词短语。如:
class ZipFileAccesser
1.1.3 变量的命名
1. 变量命名一般原则(参照匈牙利命名法)(必须)
2. 能清楚表示变量的类型
3. 能简明表达变量的涵义
4. 常量的命名(必须)
5. 常量名称全部大写,如:
public final int PORT = 2000;
6. 主要基本类型变量的命名
主要类型名称缩写格式及规则:由类型全称取一个字母组成 + 实际名称,如:
String strTemp;
对几种主要的基本类型变量命名前缀的规定(参见附录A:基本命名参考)
7. 静态变量的命名
静态变量名称以字符串“STATIC”开头,如:
static int STATICintNumber;
8. 临时变量的命名(推荐)
临时性变量格式为(类型名称缩写 + Temp),如:
String strTemp=”asdf”;
9. 对象命名规则:
第一原则:当类名是由多单词组成,取每个单词的首字母,将其小写形式组合而成变量名称的前缀,当对象是由单个单词组成时,取整个单词的小写形成作为变量名的前缀,如:
PrintWriter pwTemp;
Socket socketTemp;
第二原则:如果这样命名时产生了重复现象,则和类型名称缩写提取原则类似,取三个小写辅音字母或习惯性的用法,总之以能明显区别类名称为原则。如:ServletResponse 和ServletRequest 如果按照第一原则,两个对象都可命名为srTemp,产生了重复,此时就应按照第二原则,命名为srpTemp 和srqTemp
10. 成员变量的命名
成员变量的命名原则与临时变量相同,但在可能与临时变量名称重复的调用场合,应加上this.以示区别。推荐:对大多数成员变量调用的场合加上this.以利区别,如:
public String strUserName;
this.strUserName = strUserName;
1.1.4 方法的命名
方法名的第一个字母采用小写,其他嵌入单词首字母大写。方法名应该是一个动宾结构的短语,能够简明恰当的表达出方法完成的操作。如:
public String getStudentName(…);
1.1.5 其它说明:(推荐)
一般不允许使用数字和下划线。如:
socket1,socket_num;
1.2 注释
注释采用javadoc的注释方法,所有注释放置于所注释目标的上方。
1.2.1 类/接口的头注释
/**********************************************************
* 类描述信息
* @author 作者信息
* @version 版本信息,时间
* @since 创自信息
**********************************************************/
1.2.2 属性的注释
/**
* 注释内容
*/
1.2.3 方法的头注释
/**
* @param
* @return
* @throws
*/
注:每个参数名后必须有相应的说明,格式如下:
[参数名 参数说明]
如:
…
* @param intCounter 计数器
* @param strName 变量名称
* @param blnFlag 标志位
…
返回值不必写类型,如果没有返回值或者返回值是void 就不写@return,如:
* @return 当前的计数值
异常要逐个分行写出,如:
* @throws IOException 如果发生I/O 错误
* @throws ZIPException 如果发生ZIP 文件错误
1.2.4 代码内的注释
直接在代码内出现的注释通常使得代码易于理解。通过在前面加上一个空行和一个注释行(或更多行)来概括大的操作。在需要时用每行后面的注释或者更多的注释来解释较奇特的代码。不止一行的注释通常用/*…*/,而不是//,但//在行尾注释中更好用。单行的注释用两种方法都可以,但推荐使用//。
1.2.5 未完成代码的提醒注释
/**
* @todo 注释内容
*/
1.3 代码分隔
import …
空一行
package …
空一行
class …
1. 属性与属性间空一行,方法与方法之间空一行,类与类之间空一行。先写公有属性,后写保护属性,最后写私有属性。
2. 在方法内部,几行关系紧密的代码之间应该不空行,但两个相对独立的代码块之间应空一行。一般,在方法内部每隔3~5 行就应有一个空行。两个相邻空行之间的一段代码为一个代码块,
3. 每隔代码块前面应有行注释。
4. 构造子必须放在其他所有方法前
5. 运算符两边要加一个空格,如:
intTotal = intAvg * intNum;
6. 括号两边内侧要各加一个空格,如:
void main ( String[ ] args );
7. 一行代码只允许有一条语句,但这样简单的形式除外:
int ix, iy;
8. 减少public 域的定义,尽量通过方法而不是通过对象的实例直接访问对象的域,并且所有的成员域必须加以说明。如:
String strUserName; //用户名称
1.4 代码缩进原则
1. 类名、类体、方法体依次缩进一个单位长
2. 一个单位长是四个空格(可设为一个TAB 长)
1.5 杂项格式化约定
1. 在代码块之间使用空行,通过概括注释来分隔。
2. 使行的长度限制在80个字符内。
3. 用tab来缩进、每个tab长度为4个空格。
4. 左花括号与方法名处同一行,间隔一个空格。
5. 跳转语句的标号与switch关键字对齐。
6. 像在英文中那样使用空格(关键字和逗号后面加,括号后面不加,许多操作符之间加)。
2 附录A:基本命名参考
2.1 构造名称
w 一般的匈牙利名称是由三部分组成:一个或多个前缀,一个基本标签和一个qualifier。
w 基本标签表示了变量的类型(例如,“co”表示一种颜色)。
w 前缀修饰这个类型(例如,“rg”表示体格数组,所以“rgco”表示一个颜色数组)。
w qualifier描述了这种特定类型的具体用途(例如,“rgcoGray”表示一个作为灰色使用的颜色数组)。
2.2 标准基本标签
i
|
int
|
f
|
float
|
d
|
double
|
b
|
boolean
|
ch
|
char
|
str
|
字符串String
|
bt
|
byte类型。
|
l
|
long类型 (有符号32位的值)。
|
date
|
日期型
|
enum
|
枚举型
|
2.3 标准前缀
rg/a
|
数组(来自于“range”)。例如,“rgch”表示一个字符数组。
|
c
|
某个基类条目的计数。例如,“cch”表示字符的计数。
|
d
|
某种基类的值之间的差值或delta。例如,“dx”表示两个x类型的值之间的差值。
|
此外,以下是我们在合适的时候可以在任何匈牙利前缀之前加入的特殊前缀:
m_
|
C++类的数据成员。
|
s_
|
C++类的静态数据成员。
|
2.4 标准qualifier
First
|
某集合中的第一个条目,或所关心的第一个条目(例如pchFirst)。
|
Last
|
某集合的最后一个条目,或所关心的最后一个条目(例如,pchLast)。当作为下标使用时,Last表示最后一个有效的/希望的值,所以一个循环可能是:
for (ich = ichFirst; ich <= ichLast; ich++)
|
Lim
|
某集合的元素个数上限。与Last不同,Lim不代表一个有效的值,Lim代表最后一个有效值后面的一个,所以一个循环可能是:
for (ich = ichFirst; ich < ichLim; ich++)
|
Min
|
某集合的最小元素。与First相似,但通常表示第一个有效的值,而不是要处理的第一个值。
|
Max
|
某集合的元素个数上限(与Lim相同)。不幸的是,标准的英文中的“Max”通常表示最后一个有效的值,但Max qualifier不是一个有效的值,它是最后一个有效值后面的一个。和Lim一样,典型的用法应该是:
for (ich = ichMin; ich < ichMax; ich++)
对此要特别小心。
|
Mac
|
与Max相似,但有时可用在“当前”最大值可能随时间变化的情况下。注意Mac也是代表“最后的”有效值之后的一个。
|
Mic
|
与Min相似,但有时可用在“当前”最大值可能随时间变化的情况下。
|
T
|
临时的值。这个qualifier可能会被过度使用,而不能为某些量起一个好名字,但有时少数的临时值是可以的,比如在一个典型的交换操作中。
|
TT, T3, etc.
|
这是在需要更确切的名字的时候对T的进一步滥用。这些用法应当被避免。
|
Sav
|
用于保存一个值的临时量,使该值可以在以后被恢复。例如:hwndSav = hwnd; ...; hwnd = hwndSav;
|
Null
|
特殊的0值,总是等价于0,但用于特定的目的(例如 hwndNull)。
|
Nil
|
特殊的无效值,不一定与0等价(可能是-1,或任何其它值)。为避免混淆,最好对同一类型避免既使用Null又使用Nil。
|
Src
|
某项操作的源,典型的用法是与表示目标的Dest匹配使用,如下:
*pchDest = *pchSrc
|
Dest
|
目标。参见Src。
|
Sum
|
总和。
|
3 附录B:标准代码样本
import com.sun.tools.doclets.*;
import com.sun.javadoc.*;
import java.io.*;
import java.lang.*;
import java.util.*;
/***********************************************************************
* Generate the package index page "overview-summary.html" for the right-hand
* frame. A click on the package name on this page will update the same frame
* with the "pacakge-summary.html" file for the clicked package.
*
* @author Atul M Dambalkar
* @version 1.0
* @since JDK 1.4
***********************************************************************/
public class PackageIndexWriter
extends AbstractPackageIndexWriter {
/**
* Root of the program structure. Used for "overview" documentation.
*/
private RootDoc root;
/**
* Map representing the group of packages as specified on the command line.
*
* @see Group
*/
private Map groupPackageMap;
/**
* List to store the order groups as specified on the command line.
*/
private List groupList;
/**
* Construct the PackageIndexWriter. Also constructs the grouping
* information as provided on the command line by "-group" option. Stores
* the order of groups specified by the user.
*
* @see Group
*/
public PackageIndexWriter(String filename, RootDoc root) throws IOException {
super(filename);
this.root = root;
groupPackageMap = Group.groupPackages(packages);
groupList = Group.getGroupList();
}
/**
* Generate the package index page for the right-hand frame.
*
* @param root the root of the doc tree.
*/
public static void generate(RootDoc root) throws DocletAbortException {
PackageIndexWriter packgen;
String filename = "overview-summary.html";
try {
packgen = new PackageIndexWriter(filename, root);
packgen.generatePackageIndexFile();
packgen.close();
}
catch (IOException exc) {
Standard.configuration().standardmessage.error(
"doclet.exception_encountered",
exc.toString(), filename);
throw new DocletAbortException();
}
}
/**
* Print each package in separate rows in the index table. Generate link
* to each package.
*
* @param packagedoc Package to which link is to be generated.
*/
protected void printIndexRow(PackageDoc packagedoc) {
trBgcolorStyle("white", "TableRowColor");
summaryRow(20);
bold();
printPackageLink(packagedoc);
boldEnd();
summaryRowEnd();
summaryRow(0);
printSummaryComment(packagedoc);
summaryRowEnd();
trEnd();
}
/**
* Depending upon the grouping information and their titles, generate
* separate table indices for each package group.
*/
protected void generateIndex() {
for (int i = 0; i < groupList.size(); i++) {
String groupname = (String) groupList.get(i);
List list = (List) groupPackageMap.get(groupname);
if (list != null && list.size() > 0) {
printIndexContents( (PackageDoc[]) list.
toArray(new PackageDoc[list.size()]),
groupname);
}
}
}
/**
* Print the overview summary comment for this documentation. Print one line
* summary at the top of the page and generate a link to the description,
* which is generated at the end of this page.
*/
protected void printOverviewHeader() {
if (root.inlineTags().length > 0) {
printSummaryComment(root);
p();
bold(getText("doclet.See"));
br();
printNbsps();
printHyperLink("", "overview_description",
getText("doclet.Description"), true);
p();
}
}
/**
* Print Html tags for the table for this package index.
*/
protected void printIndexHeader(String text) {
tableIndexSummary();
tableHeaderStart("#CCCCFF");
bold(text);
tableHeaderEnd();
}
/**
* Print Html closing tags for the table for this package index.
*/
protected void printIndexFooter() {
tableEnd();
p();
space();
}
/**
* Print the overview comment as provided in the file specified by the
* "-overview" option on the command line.
*/
protected void printOverviewComment() {
if (root.inlineTags().length > 0) {
anchor("overview_description");
p();
printInlineComment(root);
p();
}
}
/**
* Call {@link #printOverviewComment()} and then genrate the tag information
* as provided in the file specified by the "-overview" option on the
* command line.
*/
protected void printOverview() throws IOException {
printOverviewComment();
generateTagInfo(root, null); //new
}
/**
* Print the header for navigation bar. Also print the "-title" specified
* on command line, at the top of page.
*/
protected void printNavigationBarHeader() {
navLinks(true);
hr();
printConfigurationTitle();
}
/**
* Print the footer fornavigation bar. Also print the "-bottom" specified
* on command line, at the top of page.
*/
protected void printNavigationBarFooter() {
hr();
navLinks(false);
printBottom();
}
}
4 附录C:更多Java编程规则
选自《Think in java》,包含了大量有用的建议,帮助大家进行低级程序设计,并提供了代码编写的一般性指导:
1. 类名首字母应该大写。字段、方法以及对象的首字母应小写。对于所有标识符,其中包含的所有单词都应紧靠在一起,而且大写中间单词的首字母。例如:
ThisIsAClassName
thisIsMethodOrFieldName
若在定义中出现了常数初始化字符,则大写static final基本类型标识符中的所有字母。这样便可标志出它们属于编译期的常数。
Java包(Package)属于一种特殊情况:它们全都是小写字母,即便中间的单词亦是如此。对于域名扩展名称,如com,org,net或者edu等,全部都应小写(这也是Java 1.1和Java 1.2的区别之一)。
2. 为了常规用途而创建一个类时,请采取“经典形式”,并包含对下述元素的定义:
equals()
hashCode()
toString()
clone()(implement Cloneable)
implement Serializable
3. 对于自己创建的每一个类,都考虑置入一个main(),其中包含了用于测试那个类的代码。为使用一个项目中的类,我们没必要删除测试代码。若进行了任何形式的改动,可方便地返回测试。这些代码也可作为如何使用类的一个示例使用。
4. 应将方法设计成简要的、功能性单元,用它描述和实现一个不连续的类接口部分。理想情况下,方法应简明扼要。若长度很大,可考虑通过某种方式将其分割成较短的几个方法。这样做也便于类内代码的重复使用(有些时候,方法必须非常大,但它们仍应只做同样的一件事情)。
5. 设计一个类时,请设身处地为客户程序员考虑一下(类的使用方法应该是非常明确的)。然后,再设身处地为管理代码的人考虑一下(预计有可能进行哪些形式的修改,想想用什么方法可把它们变得更简单)。
6. 使类尽可能短小精悍,而且只解决一个特定的问题。下面是对类设计的一些建议:
(1) 数量众多的方法涉及到类型差别极大的操作:考虑用几个类来分别实现
(2) 许多成员变量在特征上有很大的差别:考虑使用几个类
7. 让一切东西都尽可能地“私有”——private。在多线程环境中,隐私是特别重要的一个因素——只有private字段才能在非同步使用的情况下受到保护。
8. 谨惕“巨大对象综合症”。对一些习惯于顺序编程思维、且初涉OOP领域的新手,往往喜欢先写一个顺序执行的程序,再把它嵌入一个或两个巨大的对象里。根据编程原理,对象表达的应该是应用程序的概念,而非应用程序本身。
9. 若不得已进行一些不太雅观的编程,至少应该把那些代码置于一个类的内部。
10. 任何时候只要发现类与类之间结合得非常紧密,就需要考虑是否采用内部类,从而改善编码及维护工作。
11. 尽可能细致地加上注释,并用javadoc注释文档语法生成自己的程序文档。
12. 避免使用“魔术数字”,这些数字很难与代码很好地配合。如以后需要修改它,无疑会成为一场噩梦,因为根本不知道“100”到底是指“数组大小”还是“其他全然不同的东西”。所以,我们应创建一个常数,并为其使用具有说服力的描述性名称,并在整个程序中都采用常数标识符。这样可使程序更易理解以及更易维护。
13. 当客户程序员用完对象以后,若你的类要求进行任何清除工作,可考虑将清除代码置于一个良好定义的方法里,采用类似于cleanup()这样的名字,明确表明自己的用途。除此以外,可在类内放置一个boolean(布尔)标记,指出对象是否已被清除。在类的finalize()方法里,请确定对象已被清除。在采取象这样的方案之前,请确定finalize()能够在自己的系统中工作(可能需要调用System.runFinalizersOnExit(true),从而确保这一行为)。
14. 在一个特定的作用域内,若一个对象必须清除(非由垃圾收集机制处理),请采用下述方法:初始化对象;若成功,则立即进入一个含有finally从句的try块,开始清除工作。
15. 若在初始化过程中需要覆盖(取消)finalize(),请记住调用super.finalize()(若Object属于我们的直接超类,则无此必要)。在对finalize()进行覆盖的过程中,对super.finalize()的调用应属于最后一个行动,而不应是第一个行动,这样可确保在需要基础类组件的时候它们依然有效。
16. 创建大小固定的对象集合时,请将它们传输至一个数组(若准备从一个方法里返回这个集合,更应如此操作)。这样一来,我们就可享受到数组在编译期进行类型检查的好处。
17. 尽量使用interfaces,不要使用abstract类。若已知某样东西准备成为一个基础类,那么第一个选择应是将其变成一个interface(接口)。只有在不得不使用方法定义或者成员变量的时候,才需要将其变成一个abstract(抽象)类。接口主要描述了客户希望做什么事情,而一个类则致力于(或允许)具体的实施细节。
18. 在构建函数内部,只进行那些将对象设为正确状态所需的工作。尽可能地避免调用其他方法,因为那些方法可能被其他人覆盖或取消,从而在构建过程中产生不可预知的结果。
19. 对象不应只是简单地容纳一些数据;它们的行为也应得到良好的定义。
20. 警惕“过早优化”。首先让它运行起来,再考虑变得更快——但只有在自己必须这样做、而且经证实在某部分代码中的确存在一个性能瓶颈的时候,才应进行优化。除非用专门的工具分析瓶颈,否则很有可能是在浪费自己的时间。性能提升的隐含代价是自己的代码变得难于理解,而且难于维护。
21. 请记住,阅读代码的时间比写代码的时间多得多。思路清晰的设计可获得易于理解的程序,但注释、细致的解释以及一些示例往往具有不可估量的价值。无论对你自己,还是对后来的人,它们都是相当重要的。如对此仍有怀疑,那么请试想自己试图从联机Java文档里找出有用信息时碰到的挫折,这样或许能将你说服。
22. 如认为自己已进行了良好的分析、设计或者实施,那么请稍微更换一下思维角度。试试邀请一些外来人士——并不一定是专家,但可以是来自本公司其他部门的人。请他们用完全新鲜的眼光考察你的工作,看看是否能找出你一度熟视无睹的问题。采取这种方式,往往能在最适合修改的阶段找出一些关键性的问题,避免产品发行后再解决问题而造成的金钱及精力方面的损失。
23. 良好的设计能带来最大的回报。简言之,对于一个特定的问题,通常会花较长的时间才能找到一种最恰当的解决方案。但一旦找到了正确的方法,以后的工作就轻松多了,再也不用经历数小时、数天或者数月的痛苦挣扎。我们的努力工作会带来最大的回报(甚至无可估量)。而且由于自己倾注了大量心血,最终获得一个出色的设计方案,成功的快感也是令人心动的。坚持抵制草草完工的诱惑——那样做往往得不偿失。