Java编码规范

1. 目的

本规范的目的是使本组织能以标准的、规范的方式设计和编码。通过建立编码规范,使每个开发人员养成良好的编码风格和习惯;并以此形成开发小组编码约定,提高程序的可靠性、可读性、可修改性、可维护性和一致性等,增进团队间的交流,并保证软件产品的质量。

 

2. 适用范围

本规范适用于XXXX有限公司技术部所有软件开发人员,在整个软件开发过程中必须遵循此规范。

 

3. 版权声明

²  本文档为共享文档,不限转载,但请保持本文档的完整性

²  您可以修改本文档以符合您或组织、公司等之实际,但请在文档中保持对本文档的引用和说明。

²  未经产品开发中心授权,任何个人、组织或单位不得将本文档用于书面发表、转载、摘录等,亦不得用于其他商业行为。

²  本人及本组织不承担任何因使用、参考本文档等而导致的任何可能责任或连带责任。

 

4. 参考资料

²  《Java 编程指南》见RUP(Rational Unified Process)中文版。

²  《Java技术手册》(Java in a Nutshell)

²  《Sun Java语言编码规范》(Java Code Conventions)

²  《Effictive Java》

²  《Java Pitfalls》

²  《Java Rules》

 

5. 概述

对于代码,首要要求是它必须正确,能够按照设计预定功能去运行;第二是要求代码必须清晰易懂,使自己和其他的程序员能够很容易地理解代码所执行的功能等。然而,在实际开发中,每个程序员所写的代码却经常自成一套,很少统一,导致理解困难,影响团队的开发效率及系统的质量等。因此,一份完整并被严格执行的开发规范是非常必须的,特别是对软件公司的开发团队而言。此规范参考自业界标准编程规范而制定。

最根本的原则:

²  代码虽然是给机器运行的,但却是给人读的!

²  运用常识。当找不到任何规则或指导方针,当规则明显不能适用,当所有的方法都失效的时侯:运用常识并核实这些基本原则。这条规则比其它所有规则都重要。常识是必不可少。

²  当出现该情况时,应当及时收集并提交,以便对本规范进行修改。

 

6. 代码组织风格  

6.1.  基本原则

代码的组织和风格的基本原则是:便于自己的开发,易于与他人的交流。

因个人习惯和编辑器等可以设置和形成自己的风格,但必须前后一致,并符合本规范的基本要求和原则。

本章所涉及到的内容一般都可在Java集成编辑环境中进行相应设置,也可由Ant等调用checkstyle等来进行自动规整。

 

6.2.  缩进

子功能块当在其父功能块后缩进。

当功能块过多而导致缩进过深时当将子功能块提取出来做为子函数。

代码中以TAB(4个字符)缩进,在编辑器中请将TAB设置为以空格替代,否则在不同编辑器或设置下会导致TAB长度不等而影响整个程序代码的格式。例如:

Table1.缩进示例

public void methodName(){

if(some condition){

for(…){

//some sentences

}//end for

}//end if

}

 

6.3.  长度

为便于阅读和理解,单个函数的有效代码长度当尽量控制在100行以内(不包括注释行),当一个功能模块过大时往往造成阅读困难,因此当使用子函数等将相应功能抽取出来,这也有利于提高代码的重用度。

单个类也不宜过大,当出现此类情况时当将相应功能的代码重构到其他类中,通过组合等方式来调用,建议单个类的长度包括注释行不超过1500行。

尽量避免使用大类和长方法。

 

6.4.  行宽

页宽应该设置为150字符。一般不要超过这个宽度, 这会导致在某些机器中无法以一屏来完整显示, 但这一设置也可以灵活调整。在任何情况下, 超长的语句应该在一个逗号后或一个操作符前折行。一条语句折行后, 应该比原来的语句再缩进一个TAB或4个空格,以便于阅读。

 

6.5.  间隔

类、方法及功能块间等应以空行相隔,以增加可读性,但不得有无规则的大片空行。操作符两端应当各空一个字符以增加可读性。

相应独立的功能模块之间可使用注释行间隔,并标明相应内容,具体参看附录的代码示例。

 

6.6.  对齐

关系密切的行应对齐,对齐包括类型、修饰、名称、参数等各部分对齐。

连续赋值时当对齐操作符。

当方法参数过多时当在每个参数后(逗号后)换行并对齐。

当控制或循环中的条件比较长时当换行(操作符前)、对齐并注释各条件。

变量定义最好通过添加空格形成对齐,同一类型的变量应放在一起。如下例所示:

Table2.对齐示例

//变量对齐-----------------------------------------------

int count = 100;

int length = 0;

String strUserName = null;

Integer[] porductCode = new Integer(2); //产品编码数组

//参数对齐----------------------------------------------

public Connection getConnection(String url,

String userName,

String password)

throws SQLException,IOException{

}

//换行对齐----------------------------------------------

public final static String SQL_SELECT_PRODUCT = “SELECT * “

+ “ FROM TProduct WHERE Prod_ID = ”

+ prodID;

//条件对齐----------------------------------------------

if( Condition1 //当条件一

&& Condition2 //并且条件二

|| Condition3){ //或者条件三

}

for(int i = 0;

i < productCount.length; //循环终止条件

i++){

}

 

7. 注释

7.   

7.1.  基本原则

²  注释应该增加代码的清晰度。代码注释的目的是要使代码更易于被其他开发人员等理解。

²  如果你的程序不值得注释,那么它很可能也不值得运行。

²  避免使用装饰性内容。

²  保持注释的简洁。

²  注释信息不仅要包括代码的功能,还应给出原因。

²  不要为注释而注释。

²  除变量定义等较短语句的注释可用行尾注释外,其他注释当避免使用行尾注释。

 

7.2.  JavaDoc

对类、方法、变量等的注释需要符合JavaDoc规范,对每个类、方法都应详细说明其功能、条件、参数等,并使用良好的HTML标记格式化注释,以使生成的JavaDoc易阅读和理解。

 

7.3.  文件注释

在每个文件的头部都应该包含该文件的功能、作用、作者、版权以及创建、修改记录等。并在其中使用SVN标记自动跟踪版本变化及修改记录等信息。注意是/**/注释而不是/***/JavaDoc注释。

Table3 文件(File)注释模板

/*

 * Copyright (C) 2006-${year} 普信恒业科技发展(北京)有限公司.

 * 本系统是商用软件,未经授权擅自复制或传播本程序的部分或全部将是非法的.

============================================================

 * FileName: ${file_name}

 * Created: [${date} ${time}] by ${user}

 * $$Id$$

 * $$Revision$$

 * $$Author$$

 * $$Date$$

============================================================

 * ProjectName: ${project_name}

 * Description:

==========================================================*/

 

7.4.  类、接口注释

在类、接口定义之前当对其进行注释,包括类、接口的目的、作用、功能、继承于何种父类,实现的接口、实现的算法、使用方法、示例程序等,在作者和版本域中使用SVN标记自动跟踪版本变化等,具体参看注释模板。

Table4 类(Types)注释模板

/**

 * Description:

 * @author ${user}

 * @version 1.0

 * <pre>

 * Modification History:

 * Date         Author      Version     Description

------------------------------------------------------------------

 * ${date}    ${user}       1.0        1.0 Version

 * </pre>

 */

 

7.5.  方法注释

依据标准JavaDoc规范对方法进行注释,以明确该方法功能、作用、各参数含义以及返回值等。复杂的算法用/**/在方法内注解出。

参数注释时当注明其取值范围等

返回值当注释出失败、错误、异常时的返回情况。

异常当注释出什么情况、什么时候、什么条件下会引发什么样的异常。

Table5 方法(Methods)注释模板

/**

 * Description:

 * @param

 * @return ${return_type}

 * @throws

 * @Author ${user}

 * Create Date: ${date} ${time}

 */

 

7.6.  其他注释

应对重要的变量加以注释,以说明其含义等。

应对不易理解的分支条件表达式加注释。不易理解的循环,应说明出口条件。过长的方法实现,应将其语句按实现的功能分段加以概括性说明。

对于异常处理当注明正常情况及异常情况或者条件,并说明当异常发生时程序当如何处理。

 

7.7.  注释参考表

Table6 注释参考表

项目

注释内容

参数

参数类型

参数用来做什么

约束或前提条件

示例

字段/属性

字段描述

注释所有使用的不变量

示例

并行事件

可见性决策

类的目的

已知的问题

类的开发/维护历史、版本

注释出采用的不变量

并行策略

编译单元

(文件)

每一个类/类内定义的接口,含简单的说明

文件名和/或标识信息

修改/维护记录

版权信息

获取成员函数

若可能,说明为什么使用滞后初始化

接口

目的

它应如何被使用以及如何不被使用

局部变量

用处/目的

成员函数注释

成员函数做什么以及它为什么做这个

哪些参数必须传递给一个成员函数

成员函数返回什么

已知的问题

任何由某个成员函数抛出的异常

可见性决策

成员函数是如何改变对象的

包含任何修改代码的历史

如何在适当情况下调用成员函数的例子

适用的前提条件和后置条件

成员函数内部注释

控制结构

代码做了些什么以及为什么这样做

局部变量

 

难或复杂的代码处理顺序

包的功能和用途

 

8. 命名

8.   

8.1.  基本原则

规范的命名能使程序更易阅读,从而更易于理解。它们也可以提供一些标识功能方面的信息,有助于更好的理解代码和应用。

1)    使用可以准确说明变量/字段/类/接口/包等的完整的英文描述符。例如,采用类似 firstName,listAllUsers 或 CorporateCustomer 这样的名字,严禁使用汉语拼音及不相关单词命名,虽然Java支持Unicode命名,但本规范规定对包、类、接口、方法、变量、字段等不得使用汉字等进行命名。

2)    采用该领域的术语。如果用户称他们的“客户” (clients) 为“顾客” (customers),那么就采用术语 Customer 来命名这个类,而不用 Client。

3)    采用大小写混合,提高名字的可读性。一般应该采用小写字母,但是类和接口的名字的首字母,以及任何中间单词的首字母应该大写。包名全部小写。

4)    尽量少用缩写,但如果一定要使用,当使用公共缩写和习惯缩写等,如实现(implement)可缩写成impl,经理(manager)可缩写成mgr等,具体参看附录之《常用缩写简表》,严禁滥用缩写。

5)    避免使用长名字(最好不超过 25 个字母)。

6)    避免使用相似或者仅在大小写上有区别的名字。

7)    避免使用数字,但可用2代替to,用4代替for等,如:go2Jsp。

 

8.2.  文件、包

1)    文件名当与其类严格相同,所有单词首字母大写。

2)    包名一般以项目或模块名命名,少用缩写和长名,一律小写。

3)    基本包:org.skyinn,所有包、文件都从属于此包。

4)    包名按如下规则组成:

[基本包].[项目名].[模块名].[子模块名]...

如:

org.skyinn.quasar

org.skyinn.skyhome.dao.hibernate

5)    不得将类直接定义在基本包下,所有项目中的类、接口等都当定义在各自的项目和模块包中。

6)    自主开发程序的命名规则为:com.creditease.XXXX

 

8.3.  类、接口

所有单词首字母大写。使用能确切反应该类、接口含义、功能等的词。一般采用名词。

接口文件名前添加大写字母“I”,其后使用able。

 

8.4.  字段

8.4.1.  常量

采用完整的英文大写单词,在词与词之间用下划线连接,如:DEFAULT_VALUE。

 

8.4.2.  变量和参数

对不易清楚识别出该变量类型的变量应使用类型缩写作其前缀,如字符串使用strXXX,boolean使用isXXX,hasXXX等等。除第一各个单词外其余单词首字母大写。

对私有实例变量可使用_前缀,但在其存取方法中则应该将其前缀去掉。

 

8.4.3.  组件/部件

应采用完整的英文描述符命名组件(接口部件),遵循匈牙利命名法则。

如:btnOK,lblName。

 

8.4.4.  集合

一个集合,例如数组和矢量,应采用复数命名来表示队列中存放的对象类型。命名应采用完整的英文描述符,名字中所有非开头的单词的第一个字母应大写,适当使用集合缩写前缀。如:

Vector vProducts = new Vector(); //产品向量

Array aryUsers = new Array(); //用户列表

 

8.4.5.  神秘的数

我们在程序里经常会用到一些量,它是有特定的含义的。例如,现在我们写一个薪金统计程序,公司员工有50人,我们在程序里就会用50这个数去进行各种各样的运算。在这里,50就是"神秘的数"。当别的程序员在程序里看到50这个数,将很难知道它的含义,造成理解上的困难。

在程序里出现"神秘的数"会降低程序的可读性、可维护性和可扩展性,故规定不得出现此类"神秘的数"。避免的方法是把神秘的数定义为一个常量。注意这个常量的命名应该能表达该数的意义,并且应该全部大写,以与对应于变量的标识符区别开来。例如上面50这个数,我们可以定义为一个名为NUM_OF_EMPLOYEES的常量来代替。这样,别的程序员在读程序的时候就可以容易理解了。

 

8.4.6.  其他

命名时应使用复数来表示它们代表多值。如:orderItems。

 

8.5.  方法

方法的命名应采用完整的英文描述符,大小写混合使用:所有中间单词的第一个字母大写。方法名称的第一个单词常常采用一个有强烈动作色彩的动词。

取值类使用get前缀,设值类使用set前缀,判断类使用is(has)前缀。

例: getName()

setSarry()

isLogon()

方法参数建议顺序:(被操作者,操作内容,操作标志,其他⋯)

例:public void replace(String sourceStr, //源字串

String oldStr, //被替换字串

String newStr){ //替换为字串

 

8.6.  异常

异常类名由表示该异常类型的单词和Exception组成,如ActionException。

异常实例一般使用e、ex等,在多个异常时使用该异常名或简写加E,Ex等组成,如:

SQLEx

ActionEx

 

8.7.  命名约定表

Table7 命名约定表

操作项

命名约定

示例

 

参数

使用传递值/对象的完整的英文描述符。

userID

 

字段/属性

字段采用完整的英文描述,第一个字母小写,任何中间单词的首字母大写。

firstName

 

布尔型的获取成员函数

所有的布尔型获取函数必须用单词 is(has)做前缀。

isString()

hasMoney()

 

采用完整的英文描述符,所有单词的第一个字母大写。

Customer

 

编译单元文件

使用类或接口的名字,或者如果文件中除了主类之外还有多个类时,加上前缀 java 来说明它是一个源码文件。

Customer.java

 

部件/组件

使用完整的英文描述来说明组件的用途,将组件类型使用匈牙利命名法则作其前缀。

btnOK,cboTypeList

 

构造函数

使用类名

Customer()

 

析构函数

Java 没有析构函数,但一个对象在垃圾收集时,调用成员函数 finalize() 。

finalize()

 

异常类名

由表示该异常类型等的单词和Exception组成

SQLException

ActionException

 

异常实例名

通常采用字母 e 、ex表示异常。

多个异常时使用异常名或其简写加E、Ex等构成

e

SQLEx

 

静态常量字段(常量)

全部采用大写字母,单词之间用下划线分隔。采用静态常量获取成员函数。

DEFAULT_NAME

 

获取成员函数

被访问字段名的前面加上前缀 get。

getUserName()

 

接口

采用完整的英文描述符说明接口封装,所有单词的第一个字母大写。使用I前缀,其后使用able,或者 er等后缀,但这不是必需的

IRunnable

IPrompter

 

局部变量

采用完整的英文描述符,第一个字母小写,但不要隐藏已有字段。例如,如果有一个字段叫 firstName,不要让一个局部变量叫 firstName。

strName,totalMoney

循环计数器

通常采用字母 i,j,k 或者 counter,index

i,j,k,count,index

采用完整的英文描述符,所有单词都小写,多个单词以下划线相连。所有包都属于

org.skyinn

org.skyinn.quasar

org.skyinn.skyhome.dao

成员函数

采用完整的英文描述说明成员函数功能,第一个单词尽可能采用一个生动的动词,除第一个单词外,每个单词第一个字母小写。

openFile()

addUser()

设置成员函数

被访问字段名的前面加上前缀 set。

setName()

setPower()

 

9. 声明

9.   

9.1.  基本原则

声明的基本原则是遵守Java语言规范,并遵从习惯用法。

 

9.2.  包

在导入包时当完全限制代码所使用的类的名字,尽量少用通配符的方式,但导入一些通用包,或用到一个包下大部分类时,则可是使用通配符方式,如:

import org.skyinn.quasar.services.Service;

import java.util.*;

同一包中的类导入时当声明在一起,可由编辑器自动完成此功能。

重要的包当添加注释。

 

9.3.  类、接口

类、接口定义语法规范:

[可见性][('abstract'|'final')] [Class|Interface] class_name

[('extends'|'implements')][父类或接口名]{

}

如:

public class LoginAction extends BaseAction implemnets ActionListener {

}

 

9.4.  方法

良好的程序设计应该尽可能减小类与类之间耦合,所遵循的经验法则是:尽量限制成员函数的可见性。如果成员函数没必要公有 (public),就定义为保护 (protected);没必要保护 (protected),就定义为私有 (private)。

方法定义语法规范:

[可见性][('abstract'|'final')] ['synchronized'][返回值类型] method_name(参数列表)[('throws')][异常列表]{

// 功能模块

}

 

如:

public List listAllUsers() throws DAOException{}

若有toString(),equals(),hashCode(),colone()等重载自Object的方法,应将其放在类的最后。

声明顺序:

构造方法

静态公共方法

静态私有方法

受保护方法

私有方法

继承自Object的方法

 

9.5.  字段

字段定义语法规范:

[(‘public’|’private’|’protected’)]

 

[(‘final’|’volatile’)][‘static’][‘transient’]

data_type field_name [ ‘=’ expression] ‘;’

若没有足够理由,不要把实例或类变量声明为公有。公共和保护的可见性应当尽量避免,所有的字段都建议置为私有,由获取和设置成员函数(Getter、Setter)访问。

不允许“隐藏”字段,即给局部变量所取的名字,不可与另一个更大范围内定义的字段的名字相同(或相似)。例如,如果把一个字段叫做 firstName ,就不要再生成一个局部变量叫做 firstName,或者任何易混肴的名字,如 fistName。

数组声明时当将"[]"跟再类型后,而不时字段名后,如:

Integer[] ai = new Integer (2); //一个整数对象数组,用于...

Integer aj[] = new Integer (3); //不要用这种数组声明方式

一行代码只声明一个变量,仅将一个变量用于一件事。

声明顺序:

常量

类变量

实例变量

公有字段

受保护字段

私有字段

可以将私有变量声明在类或接口的最后。

注意受保护变量、私有变量、“包”变量间的区别。

 

9.6.  示例

Table8

//常量---------------------------------------------------

public final static double PI = 3.141592653589793;

// -------------------------------------------------类变量

protected static String key = “Love”;

// -----------------------------------------------实例变量

//共有字段-----------------------------------------------

public String userName = “Tom”;

//受保护字段---------------------------------------------

protected float price = 0.0;

//友元字段-----------------------------------------------

Vector vPorducts = null;

//私有字段-----------------------------------------------

private int count;

//构造方法-----------------------------------------------

public Constructor(){

}

//公共方法-----------------------------------------------

public String getUserName(){

}

//友元方法-----------------------------------------------

void createProduct(){

}

//受保护方法---------------------------------------------

protected void convert(){

}

//私有方法-----------------------------------------------

private void init(){

}

//重载Object方法----------------------------------------

public String toString(){

}

//main方法-----------------------------------------------

public static void main(String[] args){

}

 

 

10.    类与接口

10.     

10.1.    基本原则

类的划分粒度,不可太大,造成过于庞大的单个类,也不可太细,从而使类的继承太深。一般而言,一个类只做一件事;另一个原则是根据每个类的职责进行划分,比如用User来存放用户信息,而用UserDAO来对用户信息进行数据访问操作(比如存取数据库),用UserBroker来封装用户信息的业务操作等等。

多使用设计模式,随时重构。

多个类中使用相同方法时将其方法提到一个接口中或使用抽象类,尽量提高重用度。

将不希望再被继承的类声明成final,例如某些实用类,但不要滥用final,否则会对系统的可扩展性造成影响。

将不希望被实例化的类的缺省构造方法声明成private。

 

10.2.    抽象类与接口

一般而言:接口定义行为,而抽象类定义属性和公有行为,注意两者间的取舍,在设计中,可由接口定义公用的行为,由一个抽象类来实现其部分或全部方法,以给子类提供统一的行为定义,可参考Java集合等实现。

多使用接口,尽量做到面向接口的设计,以提高系统的可扩展性。

 

10.3.    继承与组合

尽量使用组合来代替继承,一则可以使类的层次不至于过深,而且会使类与类,包与包之间的耦合度更小,更具可扩展性。

 

10.4.    构造函数和静态工厂方法

当需要使用多个构造函数创建类时,建议使用静态工厂方法替代这些构造方法(参考《Effictive Java》 Item1),例如:

public class User{

public User(){

super();

//do somethings to create user instance

}

public static User getInstance(String name,String password){

User u = new User();

u.setName(name);

u.setPassword(password);

return u;

}

参看String,Boolean的实现等:String.valueOf(Long l),Boolean.valueOf(String)...

 

10.5.    toString( ),equals( ),hashCode( )...

每个类都应该重载toString()方法,以使该类能输出必要和有用的信息等。

/**

* @see java.lang.Object#toString()

*/

public String toString() {

final StringBuffer sb = new StringBuffer("Actor:[");

sb.append("ID = ").append(_id)

.append(",Name = ").append(_name)

.append(']');

return sb.toString();

}

若一个类需要重载equals()方法,则必须同时重载hashCode()方法实现方式参考《Effictive Java》Item7,Item8

/**

* @see java.lang.Object#equals(java.lang.Object)

*/

public boolean equals (Object obj) {

//空值

if( null == obj){

return false;

}

//引用相等

if (obj == this) {

return true;

}

//判断是否为当前类实例

if (!(obj instanceof Actor)) {

return false;

}

//若ID相等则认为该对象相等

return this._id == ((Actor) obj).getId ();

}

/**

* @see java.lang.Object#hashCode()

*/

public int hashCode () {

int result = 17;//init result

//String 对象hashCode

result = (37 * result) + _name.hashCode ();

//数值

result = (37 * result) + (int) (_id ^ (_id >>> 32));

//String 对象hashCode

result = (37 * result) + _description.hashCode ();

return result;

}

这些方法的重载实现也可参考如下实现(使用Jakarta commons-lang):

 

10.6.    Singleton Class

单例类使用如下方式声明,并将其缺省构造方法声明成private:

public class Singleton{

private static Singleton instance = new Singleton();

// 私有缺省构造方法,避免被其他类实例化

private Singleton(){

//do something

}

public static Singleton getInstance(){

if(null == instance){

instance = new Singleton;

}

return instance;

}

}//EOC Singleton

 

单例类若需要实现序列化,则必须提供readResolve()方法,以使反序列化出来的类仍然是唯一的实例,参见《Effictive Java》 Item57。

 

11.    方法

11.     

11.1.    基本原则

一个方法只完成一项功能,在定义系统的公用接口方法外的方法应尽可能的缩小其可见性。

避免用一个类是实例去访问其静态变量和方法。

避免在一个较长的方法里提供多个出口:

//不要使用这钟方式,当处理程序段很长时将很难找到出口点

if(condition){

return A;

}else{

return B;

}

//建议使用如下方式

String result = null;

if(condition){

result = A;

}else{

result = B;

}

return result;

 

11.2.    参数和返回值

避免过多的参数列表,尽量控制在5个以内,若需要传递多个参数时,当使用一个容纳这些参数的对象进行传递,以提高程序的可读性和可扩展性。

参数类型和返回值尽量接口化,以屏蔽具体的实现细节,提高系统的可扩展性,例如:

public void joinGroup(List userList){}

public List listAllUsers(){}

 

12.    表达式与语句

12.     

12.1.    基本原则

表达式和语句当清晰、简洁,易于阅读和理解,避免使用晦涩难懂的语句。

每行至多包含一条执行语句,过长当换行。

避免在构造方法中执行大量耗时的初始化工作,应当将这中工作延迟到被使用时再创建相应资源,如果不可避免,则当使用对象池和Cache等技术提高系统性能。

避免在一个语句中给多个变量赋相同的值。它很难读懂。

不要使用内嵌(embedded)赋值运算符试图提高运行时的效率,这是编译器的工作。

尽量在声明局部变量的同时初始化。唯一不这么做的理由是变量的初始值依赖于某些先前发生的计算。

一般而言,在含有多种运算符的表达式中使用圆括号来避免运算符优先级问题,是个好方法。即使运算符的优先级对你而言可能很清楚,但对其他人未必如此。你不能假设别的程序员和你一样清楚运算符的优先级。

不要为了表现编程技巧而过分使用技巧,简单就好。

 

12.2.    控制语句

判断中如有常量,则应将常量置与判断式的左侧。如:

if ( true == isAdmin())...

if ( null == user)...

尽量不使用三目条件判断。

所有if语句必须用{}包括起来,即便是只有一句:

if (true){

//do something......

}

if (true)

i = 0; //不要使用这种

当有多个else分句时当分别注明其条件,注意缩进并对齐,如:

//先判断i 是否等于1

if (i == 1){//if_1

//.....

}//然后判断i == 2

else if (i == 2){

//i == 2说明。。。。。。

j = i;

}//如果都不是(i > 2 || i < 1)

else{

//说明出错了

//....

}//end if_1

过多的else分句请将其转成switch语句或使用子函数。

每当一个case顺着往下执行时(因为没有break语句),通常应在break语句的位置添加注释。如:

switch (condition) {

case ABC:

//statements;

//继续下一个CASE

case DEF:

//statements;

break;

case XYZ:

//statements;

break;

default:

//statements;

break;

}//end switch

12.3.    循环语句

循环中必须有终止循环的条件或语句,避免死循环。

当在for语句的初始化或更新子句中使用逗号时,避免因使用三个以上变量,而导致复杂度提高。若需要,可以在for循环之前(为初始化子句)或for循环末尾(为更新子句)使用单独的语句。

因为循环条件在每次循环中多会执行一次,故尽量避免在其中调用耗时或费资源的操作,比较一下两种循环的差异:

//不推荐方式____________________________________________

while(index < products.getCount()){

//每此都会执行一次getCount()方法,

//若此方法耗时则会影响执行效率

//而且可能带来同步问题,若有同步需求,请使用同步块或同步方法

}

//推荐方式______________________________________________

//将操作结构保存在临时变量里,减少方法调用次数

final int count = products.getCount();

while(index < count){ }

 

13.    错误与异常

13.     

13.1.    基本原则

通常的思想是只对错误采用异常处理:逻辑和编程错误,设置错误,被破坏的数据,资源耗尽,等等。

通常的法则是系统在正常状态下以及无重载和硬件失效状态下,不应产生任何异常。

最小化从一个给定的抽象类中导出的异常的个数。

对于经常发生的可预计事件不要采用异常。

不要使用异常实现控制结构。

确保状态码有一个正确值。

在本地进行安全性检查,而不是让用户去做。

若有finally子句,则不要在try块中直接返回,亦不要在finally中直接返回。

 

13.2.    已检查异常与运行时异常

已检查异常必须捕捉并做相应处理,不能将已检查异常抛到系统之外去处理。

对可预见的运行时异常当进行捕捉并处理,比如空指针等。通常,对空指针的判断不是使用捕捉NullPointException的方式,而是在调用该对象之前使用判断语句进行直接判断,如:

//若不对list是否为null进行检查,则在其为null时会抛出空指针异常

if(null != list && 0 < list.size()){

for(int i = 0; i < list.size(); i++){

}

}

建议使用运行时异常(RuntimeException)代替已检查异常(CheckedException),请参考网络资源以对此两种异常做更深入理解。

 

13.3.    异常的捕捉与处理

捕捉异常是为了处理它,不要捕捉了却什么都不处理而抛弃之,最低限度当向控制台输出当前异常,如果你不想处理它,请将该异常抛给它的调用者,建议对每个捕捉到的异常都调用printStackTrace()输出异常信息,避免因异常的湮没。

多个异常应分别捕捉并处理,避免使用一个单一的catch来处理。如:

try {

//do something

}catch(IllegalStateException IllEx){

IllEx.printStackTrace();

//deal with IllEx

}catch(SQLException SQLEx){

SQLEx.printStackTrace();

throw SQLEx; //抛给调用者处理

}finally{

//释放资源

}

 

14.    测试与Bug跟踪

14.     

14.1.    基本原则

测试不通过的代码不得提交到SVN库或者发布。

不得隐瞒、忽略、绕过任何Bug,有Bug不一定是你的错,但有了Bug不作为就是你的不对了。

多做测试,测试要完全,尽量将各种可能情况都测试通过,尽量将可能的Bug在开发中捕捉并处理掉。

测试要保证可再测试性。

测试应当对数据库等资源不留或少留痕迹,例如,当测试添加一个用户时,在其成功后当及时从数据库中删除该记录,以避免脏数据的产生(由此衍生的一个经验是将添加、获取、删除一起测试)。

对关键功能必须测试并通过,对辅助功能及非常简单之功能可不做测试。

 

14.2.    Junit单元测试

在Java应用中,单元测试使用Junit及其衍生工具。

在TestCase的setUp()中初始化应用,在tearDown()中释放资源。可由一个基础TestCase完成这些任务,其他TestCase继承之。

 

14.3.    持续集成

由持续集成工具来完成代码编译与自动部署,参见《持续集成规程》。

 

14.4.    Bug跟踪和缺陷处理

当系统出现Bug时当由该Bug的负责人(代码负责人)尽快修改之。

Bug的处理根据其优先级高低和级别高低先后处理。

Bug级别和优先级别参见《软件测试规程》。

禁止隐瞒Bug。

 

15.    性能与安全

15.     

15.1.    基本原则

性能的提升并不是一蹴而就的,而是由良好的编程积累的,虽然任何良好的习惯和经验所提升的性能都十分有限,甚至微乎其微,但良好的系统性能却是由这些习惯等积累而成,不积细流,无以成江海!

 

15.2.    String与StringBugffer

不要使用如下String初始化方法:

String str = new String(“abcdef”);

这将产生两个对象,应当直接赋值:

String str = “abcdef”;

在处理可变 String 的时候要尽量使用 StringBuffer 类,StringBuffer 类是构成 String 类的基础。String 类将 StringBuffer 类封装了起来,(以花费更多时间为代价)为开发人员提供了一个安全的接口。当我们在构造字符串的时候,我们应该用 StringBuffer 来实现大部分的工作,当工作完成后将 StringBuffer 对象再转换为需要的 String 对象。比如:如果有一个字符串必须不断地在其后添加许多字符来完成构造,那么我们应该使用 StringBuffer 对象及其 append() 方法。如果我们用 String 对象代替 StringBuffer 对象的话,将会花费许多不必要的创建和释放对象的 CPU 时间。

 

15.3.    集合

避免使用Vector和HashTable等旧的集合实现,这些实现的存在仅是为了与旧的系统兼容,而且由于这些实现是同步的,故而在大量操作时会带来不必要的性能损失。在新的系统设计中不当出现这些实现,使用ArrayList代替Vector,使用HashMap代替HashTable。

若却是需要使用同步集合类,当使用如下方式获得同步集合实例:

Map map = Collections.synchronizedMap(new HashMap());

由于数组、ArrayList与Vector之间的性能差异巨大(具体参见《Java fitball》),故在能使用数组时不要使用ArrayList,尽量避免使用Vector。

 

15.4.    对象

避免在循环中频繁构建和释放对象。

不再使用的对象应及时销毁。

如无必要,不要序列化对象。

 

15.5.    同步

在不需要同步操作时避免使用同步操作类,如能使用ArrayList时不要使用Vector。

尽量少用同步方法,避免使用太多的 synchronized 关键字。

尽量将同步最小化,即将同步作用到最需要的地方,避免大块的同步块或方法等。

 

15.6.    final

将参数或方法声明成final可提高程序响应效率,故此:

注意绝对不要仅因为性能而将类、方法等声明成final,声明成final的类、方法一定要确信不再被继承或重载!

不需要重新赋值的变量(包括类变量、实例变量、局部变量)声明成final

所有方法参数声明成final

私有(private)方法不需要声明成final,若方法确定不会被继承,则声明成final。

 

15.7.    垃圾收集和资源释放

不要过分依赖JVM的垃圾收集机制,因为你无法预测和知道JVM在什么时候运行GC。

尽可能早的释放资源,不再使用的资源请立即释放。

可能有异常的操作时必须在try的finally块中释放资源,如数据库连接、IO操作等:

Connection conn = null;

try{

//do something

}catch(Exception e){ //异常捕捉和处理

e.printStackTrack();

}finally{

//判断conn等是否为null

if(null != conn){

conn.close();

}

}//end try...catch...finally

 

16.    持久层

16.     

16.1.    数据库主键生成策略

主键类型:

字符串/整型值。推荐使用字符型做主键,若有性能要求,允许使用数值类型

生成机制:

字符串:生成UUID,或者GUID

整型值:使用数据库序列

生成方法:

根据公司数据库系统的实际情况,并遵循复杂度最小化原则,分别就hibernate及iBatis两个持久层框架的主键生成策略进行限定:

1.   hibernate

要求使用以下主键生成策略的任意一种:

(1)sequence(序列):用于为整数类型的主键生成

数据库中的语法如下:

Oracle:create sequence seq_name increment by 1 start with 1;

需要主键值时可以调用seq_name.nextval或者seq_name.curval得到,数据库会帮助我们维护这个sequence序列,保证每次取到的值唯一,示例如下:

insert into tbl_name(id, name) values(seq_name.nextval, ‘Jimliu’);

<id name="id" column="id" type="long">

<generator class="sequence">

<param name="sequence">seq_name</param>

</generator>

</id>

如果我们没有指定sequence参数,则Hibernate会访问一个默认的sequence,是hibernate_sequence,我们也需要在数据库中建立这个sequence。

此外,sequence还可以有另外一个参数是paramters,可以查看Hibernate的API了解它的用法,见org.hibernate.id.SequenceGenerator。

调用数据库的sequence来生成主键,要设定序列名,不然hibernate无法找到:

<param   name="sequence">NAME_SEQ</param>

(2)uuid.hex:用于为字符串类型的主键生成

使用一个128-bit的UUID算法生成字符串类型的标识符,UUID被编码成一个32位16进制数字的字符串。UUID包含:IP地址、JVM启动时间、系统时间(精确到1/4秒)和一个计数器值(JVM中唯一) hibernate会算出一个128位的唯一值插入。

配置示例如下:

<id name="id" column="id">

<generator class="uuid.hex" />

</id>

2.   iBatis

要求使用以下主键生成策略:

(1)sequence(序列):用于为整数类型的主键生成

由于公司项目数据库多数使用oracle,可使ibatis中对oracle数据表的主键自增长的使用方法。

假设有用户表User,该表主键为userid,当我们希望该字段能够自动增长而不需要人工插值的情况下,具体步骤如下:

先建立序列 Create sequence seq_user_userid minvalue 1 maxvalue 999999999 increment by 1 start with 1;

在需要对该表插入新记录的时候,可使用序列seq_user_userid的nextval来代替主键字段的插入,如我们可以在相应的xml配置文件中写:

<insert id=”insert_user” parameterClass=”user”>

<selectKey resultClass=”int ” type=”pre” keyProperty=”userid”>

             Select seq_user_userid.nextval as value from dual //获取序列的最新值,并赋给userid

</selectKey>

        Insert into user(userid,name,age) values (#userid#,#name#,#age#) //将userid代表的序列值插入新记录的主键字段

</insert>

在你的业务逻辑Dao中,具体方法调用insert_user项执行数据库操作即可。

(2)UUID:用于为字符串类型的主键生成

java.util.UUID.randomUUID().toString().replaceAll("-", "").toUpperCase()

生成结果:C8F6E30AE0994A16A87C4BFB7FC92F58

3. ORACLE中,要求使用以下主键生成策略

(1)   数值型主键生成方法:

略,同序列的使用。

(2)   字符型主键生成方法:

select sys_guid() from dual  ,结果示例:47962A3B0FD34B5F800D4D0CEC8D7368

转载于:https://my.oschina.net/u/1398304/blog/747964

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值