Java 编码规范

2010-05-06 11:12

为什么要有编码规范?编码规范对于程序员而言尤为重要,主要有以下几个原因:

重 要性
一个软件的生命周期 中,80%的花费在于维护。
几乎没有任何一个软 件,在其整个生命周期中,均由最初的开发人员来维护。
编码规范可以改善软件 的可读性,可以让程序员尽快而彻底地理解新的代码。
如果你将源码作为产品 发布,就需要确任它是否被很好的打包并且清晰无误。
简 而言之
代码是写给别人看的!

代码规范是高代码质量和高开发效率的前提。代码规范是团队成员之间无阻碍交流的保证。每个软件开发人员必须一致遵守编码规范。

1. 命名规范

定义这个规范的目的是让项目中所有的文档都看起来像一个人写的,增加可读性,减少项目组中因为换人而带来的损失。

1.1 包的命名

包名命名必须全部是英文的小写字母,由名词或名词缩写组成。按照国际规例,包的命名由组织性质,组织名称缩写或全名,系统名称组成。比如公司一般用 com,社团用net,非盈利的组织用org。包末端的名称一般是根据包含的类或接口的职责,功能来名称,比如业务逻辑的,可以是service或 manager,具体实现可能是service.impl。包命名的基本原则是能够抽象出包管辖的类的职责和功能。简单的说就是一看包就知道下面的类是做 什么的。
一些常见的包的命名:

org.apache.commons.logging
org.springframework.dao
org.hibernate

1.2 类和接口的命名

类和接口的名称必须以大写的英文字母开头,必须是名词或名词短语,多个名词存在的时候名词的第一字母大写。禁止在名称中出现形容词、动词和动词衍生 形式,比如DeleteUser,UpdateDClass,GetNode,MyNode,YourTree来命名。类和接口名称要有实际的意义,根据 名称就大概知道这个类的职责是什么。严禁使用Page1,Page2来命名。对于Domain model,直接就是对象的名称,比如Book,Address之类,如果是业务接口,那就是BookManager或者BookService,业务实 现类就是BookManagerImpl或者BookServiceImpl。这里的Impl是Implement的缩写。对于一些工具类,可能是会加上 Utils,比如FileUtils,StringUtils,这些工具类的方法一般都是静态方法,比较著名的 org.apache.commons.lang,下面有很多工具类。如果是异常类,一般都以Exception为后缀,比如 UniqueException,一看名称大概就是这个一个违反唯一约束的异常类。
以下常见的命名方法:

org.hibernate.Criteria
javax.servlet.http.HttpSession
org.apache.tapestry.util.ContentType
org.apache.commons.lang.StringUtils
org.springframework.dao.DataIntegrityViolationException

1.3 变量的命名

变量以名词或名词短语组成,第一个英文字母必须小写。如果是静态变量,则全部大写,用下滑划线分割。禁止在名称中出现形容词、动词和动词衍生形式。 实例的名称尽量用类的名字或者有意义的英文单词,比如User user 和 UserService userService。严禁使用无意义的字母活数字,如a,b,c,x,y,a1,a2,myNode,myTree来命名。如果是实例结合或数组,那 一般用类名的复数形式或者后缀加上list和map,比如userList,userMap,users,尽量避免直接用map,list作为实例名称。

常见的命名方法:

byte[] buffer
public static final int INDEX = 0
private final boolean isCascadeDeleteEnabled;
EntityKey key = new EntityKey()

1.4 参数的命名

参数的名字和变量的命名规范一致。

1.5 数组的命名

数组应该总是用下面的方式来命名: 
byte[] buffer;
而不是: 
byte buffer[];

1.6 方法的命名

方法的命名以动词和动词短语组成,第一个英文字母必须小写,但有一个例如,以is和has为前缀的方法名。方法名尽量让客户程序员看见名称就大概知 道这个方法的行为。Is和has为前缀的方法返回的都是boolean或Booelan.
常见的方法命名:

protected boolean hasPostCommitEventListeners()
private static EntityPersister create
publicboolean isAdmin()

2. 文件样式

2.1 Package/Imports

Package 行要在 import 行之前,import 中标准的包名要在本地的包名之前,而且按照字母顺序排列。禁止使用*方式,如 java.io.* 。导入定义应当是类的全称

import java.util.ArrayList;
import java.util.Hashtable;

2.2 类和接口

类和接口需要填写注释,一般是用来解释类的职责和使用方法,而且要尽可能的详细。

/**
* A class representing a set of packet and byte counters
* It is observable to allow it to be watched, but only
* reports changes when the current set is complete
* @author Ken
*/
public interface DClassDao extends GenericDao<DClass, Long>

2.3成员变量

public 的成员变量必须生成文档。proceted、private和 package 定义的成员变量如果名字含义明确的话,可以没有注释。

/**
* Packet counters
*/
protected int[] packets;

2.4构造函数

构造函数应该用递增的方式写(比如:参数多的写在后面)。 访问控制符("public", "private" 等.) 和 任何 "static", "final" 或 "synchronized" 应该在一行中,并且方法和参数另写一行,这样可以使方法和参数更易读。

public CounterSet(int size){
this.size = size;
}

2.5方法

接口的方法默认是public,定义接口方法可以不定义访问控制符号。类的方法必须指定访问控制符。所有的方法必须写方法注释,但接口和抽象类的实 现方法可以不写。

3. 编程惯例

3.1 public private final protected使用

任何一个类,接口,方法,变量必须要有访问控制符号,如果是提供给客户程序使用,那么申明为public,如果只是内部过渡行为,那么申明为 private.这里有一个原则,尽量使用private,少使用public.

3.2 代码中不能出现无用的包和类、变量

具体到Eclipse工作区内禁止出现任何红色错误和黄色警告的标记出现。

3.3 禁止在一个Java文件中编写多个类。

内部类和匿名类除外,但建议少用。

3.4 继承一般只能继承抽象类一般不能超过两层继承

继承非抽象类,重载了非抽象方法,在实例的向上转型和向下转型会带来一些意想不到的困扰,建议经验不足者慎用。继承是一种无契约无保证的方法重用, 父类方法行为的改变不会通知到子类和子类的客户程序。多用接口,少用继承。

3.5 if,if-else,if else-if else语句(if, if-else, if else-if else Statements)

if-else语句应该具有如下格式:

if (condition) {
statements;
} else if (condition) {
statements;
} else{
statements;
}

注意:if语句总是用

"{""}"

括起来,避免使用如下容易引起错误的格式:

if (condition)
statement;

3.5 while,if, for,相互嵌套的层次不能超过3层

while(true){
if(size > 0){
for(User user : userList){
if(user.name.isEmpty()){
……..
}
}
}
}

3.6 java代码中不能出现SQL和HQL

SQL和HOL在Java代码中很难排版和维护,禁止在代码中出现。

String sql = "select c.* from metaone_dclass "
Session session = getSession(false);
Query query = session.createSQLQuery(sql).addEntity(DClass.class);
query.setParameter("id", id);
return query.list();

3.7 禁止直接在控制台打印。

禁止使用System.out.和System.error.,e.printStackTrace()。请使用Log4j来处理。

3.8 Collection和Map之间尽可能避免相互嵌套,禁止相互嵌套超过一层。

Map<String, Map<? extends Object, Map<Integer, Object>>> authorities = authorization.getAuthorities();

3.9 方法参数的个数禁止超过3个。

错误范例:

public List<DClass> findDClass(String name,
List<DAttribute> attributes, Long mid, int firstResult,
int maxResults, boolean matchCase)

3.10 import的类不能超过30个。

此项作为建议。

3.11 页宽限制

页宽应该设置为80字符。源代码一般不会超过这个宽度, 并导致无法完整显示,但这一设置也可以灵活调整。在任何情况下,超长的语句应该在一个逗号或者一个操作符后折行。一条语句折行后,应该比原来的语句再缩进 2个字符。

3.12 {} 对限制

{} 中的语句应该单独作为一行. {应当在一行的结束出现,不出现在新行开始,应当在一个新行中开始和结束, 即使代码体中只有一行,也需要使用{}例如, 下面的第1行是错误的, 第2行是正确的:

错误范例

if (i>0) { i ++ };

正确范例

if (i>0) { 
i ++
};

3.13 类代码长度和方法代码长度

每个Java程序一般不的超过200行,每个Java方法一般不的超过50行。如果超过类超过200行,方法超过50行,那就需要重新审查一下设 计,类的职责是否单一,方法是否承载的过多的功能。臃肿的类和方法的出现,一般都伴随的“相同或相似的代码”出现。如果出现了相同和相似的代码,那么就需 要的相同的代码抽象出来成一个独立的类或方法。

3.14 代码禁止出现TODO

这些代码一般都是IDE自动生成。

4. 注释规范

注释主要是用于描述类的职责,方法的行为。注释应被用来给出代码的总括,并提供代码自身没有提供的附加信息。注释应该仅包含与阅读和理解程序有关的 信息。

禁止在代码中出现“到此一游”的现象,比如在代码中注释“某人在某时做某事”,这部分注释内容应该提交到版本管理工具中。

建 议

尽可能避免注释单行和在方法内部写注释。比如String password;//密码。如果需要注释请重新审视变量的命名。如果需要在方法内部写注释,请重新检查方法的注释是否足够详细或者方法的设计是否存在问 题。

注 意

频繁的代码内注释有时反映出代码的低质量。当你觉得被迫要加注释的时候,考虑一下重构代码使其更清晰。

4.1 范例

Java 的注释:/** ...... */ 注释若干行,并写入 javadoc 文档

注释要简单明了,边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。在必要的地方注释,注释量要适中。注释的内容要清楚、明 了,含义准确,防止注释二义性。在每个源文件的头部要有必要的注释信息,包括:版本号;作者;生成日期;模块功能描述(如功能、主要算法、内部各部分之间 的关系、该文件与其它文件关系等)。

比如:

/**
* This class can be used to parse other classes containing constant definitions
* in public static final members. The <code>asXXXX</code> methods of this class
* allow these constant values to be accessed via their string names.
*
* @author Rod Johnson
* @author Juergen Hoeller
*/

4.2 Null规约

如果方法允许Null作为参数,或者允许返回值为Null,必须在JavaDoc中说明。如果没有说明,方法的调用者不允许使用Null作为参数, 并认为返回值是Null Safe(不会返回NULL)。

/**
* get object.
*
* @ return the object to found or null if not found.
*/
pubic Object get(Integer id){
...
}

4.3 注释标签语法

@author 对类的说明 标明开发该类模块的作者 
@version 对类的说明 标明该类模块的版本 
@see 对类、属性、方法的说明 参考转向,也就是相关主题 
@param 对方法的说明 对方法中某参数的说明 
@return 对方法的说明 对方法返回值的说明 
@exception 对方法的说明 对方法可能抛出的异常进行说明