1、引言
1.1、编码规范的意义
一个软件的生命周期中,80%的花费在于维护;
几乎没有任何一个软件,在其整个生命周期中,均由最初的开发人员来维护;
编码规范可以改善软件的可读性,可以让程序员尽快而彻底地理解新的代码;
1.2、适用范围
公司包括研发部、项目实施部和技术支持部在内的所有使用Java开发的程序;
其它编程语言如没有公司专门的编码规范,也应该尽量参考此规范中的要求。
2、程序注释的规范
2.1、注释类型
Ø 文档注释类型:/** */
Ø C注释类型:/* */
Ø 单行注释类型://
2.2、文件头注释
示例:
/******************************************************************
* TradeOperator.java
* Copyright 2015 by GNNT Company. All Rights Reserved.
* CreateDate:2015-4-23
* Author:LiuZhenXing
* Version:1.0.1
******************************************************************/
说明:
Ø 注释第一行为文件名;
Ø 第二行Copyright的时间为文件初始创建的年份;
Ø CreateDate为文件初始创建的日期;
Ø Author为文件初始开发工程师的名字全拼,第一个字母大写;
Ø Version为此文件的版本号,由开发工程师自行命名。由三位组成,从1.0.0开始,确保每次程序发生变更,版本号增加即可;
2.3、文件修改注释
示例:
/**
* <b>修改记录:</b>
* <p>
* <li>
* 创建申请结束合同传入信息类,并生成传入参数和其中的验证方法。
* ---- LiuZhenXing 2015-4-23Version:1.0.0
* </li>
*
* <li>
* 1.因为管理员的状态判断和合同没有任何关系,所以合同里没必要有管理员状态判断的方法。<br/>
* 所以本方法的管理员状态判断返回警告信息,通知外部自己进行判断。<br/>
* 2.修改判断状态的执行方法,将方法放到 Trade 中。
* ---- LiuZhenXing 2015-4-24Version:1.0.1
* </li>
* </p>
*
说明:
Ø 每对<li></li>对中的内容为每次的修改内容;
Ø 在每条修改记录的最后,用四个横线做分割,记录修改工程师的名字和修改日期;
Ø 修改内容按照时间从前到后排序;
Ø 文件修改记录在每个类说明之前,与类说明在同一个注释段中;
2.4、类注释
示例:
* <b>类说明:</b>
* <p>
* 本类为申请结束合同时的操作类。可以同时申请结束多个合同,所以需要传入结束合同编号列表。
* 同时,结束合同可以由买方、卖方发起,也可以由居间商、机构、会员、系统管理员代为发起。
* 所以在申请时要记录发起人类型,并且记录申请人编号。
*
* 其中申请人类型属性包括:0 合同买方、1 后台管理人员。
* 以后可能会有所变动,比如可能会增加申请人类型等,申请人定义了常量类,以方便以后的变动需要。
*
* </p>
*/
说明:
Ø 必须对类的基本功能进行说明;
Ø 说明类中重要属性使用。如示例中对合同申请人类型的说明;
2.5、类属性和常量注释
示例:
/** 默认申请人,如果不设置申请人,则默认使用本申请人代码 */
publicstaticfinal String DEFAULT_APPLYER = "admin";
/** 默认判断是否需要审核,如果不设置,则默认为不需要审核 */
publicstaticfinalbooleanDEFAULT_ISNEEDAUDIT = false;
/** 合同编号集合 */
private List<Long>tradeIDList;
/**
* 是否需要审核<br/>
* true:需要审核 false:不需要审核<br/>
* 默认为 DEFAULT_ISNEEDAUDIT (false)
*/
privatebooleanisNeedAudit = DEFAULT_ISNEEDAUDIT;
说明:
Ø 一般为单行注释,注释较长,或多行注释更清楚时,可用多行注释
2.6、方法注释
示例:
/**
* <b>方法说明:</b>
* <ul>
* 判断申请人是否有申请结束合同的权限
* </ul>
*
* @param tradeList 合同信息列表
* @return ResultVO 判断申请人是否有权限的结果集
* 结果中的 result:-1:验证失败、0:需要外部验证、1:验证通过
* 返回信息在 spring_recode.xml 的 recode 中配置
*/
public ResultVO isHaveApplyRight(List<ES_TradePO> tradeList)
说明:
Ø 必须对方法功能进行说明;
Ø 必须对方法传入各个参数的意义进行说明;
Ø 必须对返回信息进行说明;
Ø 必要时需说明方法存在的缺陷等;
2.7、方法内变量注释
示例:
// 按合同编号区分合同 Map 集合:key:合同编号 value:合同对象
Map<Long, ES_TradePO> tradeMap = new HashMap<Long, ES_TradePO>();
// 按合同买方代码区分合同 Map 集合:key:卖方交易商代码 value:合同对象集合
Map<String, List<ES_TradePO>> tradeBfirmMap = new HashMap<String, List<ES_TradePO>>();
/*
* 本列表为可能被撤销的合同集合。
* 当合同为刚申请状态时可能被撤销;
* 当合同为审核拒绝状态时可能被撤销。
*/
List<ES_TradePO> mayBeWithdowTrade = new ArrayList<ES_TradePO>();
说明:
Ø 必须对变量表达的含义进行说明;
Ø 如果是泛型,必须对泛型意义进行说明;
Ø 方法内变量即使使用“文档注释”方式,当鼠标放到变量上也不会出现提示,更不会导出到 doc 文档中,所以这里用“单行注释”或“C注释”类型;
2.8、单行注释类型
Ø 用于较短的方法内变量注释。一般放在变量上一行,单独占用一行;
Ø 用于较短的方法内单行代码说明,一般放在代码上一行,单独占用一行;特殊情况,如 case 时,也可写在变量后边,与代码同行;
2.9、程序块注释
示例:
/*
* 由于后续代码有需求,要通过合同编号获取合同信息,
* 也需要通过卖方交易商代码获取合同信息。如果每次都要遍历合同列表,
* 则会影响程序执行效率,所以以下构建了两个 Map 实例,
* 分别用合同编号和合同的卖方交易商代码作为key来保存合同信息。
* 后续使用时,可直接通过 Map 获取合同信息,无需反复遍历合同集合。
*/
说明:
Ø 用于说明一块代码实现的功能;
2.10、重要代码注释
示例:
/** ==============================验证申请人是否有权限开始======================= */
// =========================================================================
// 如果申请人类型为: 0:合同买方,则直接判断申请人是否为买方。
//
// 如果申请人类型为:1:后台管理员,则调用 TradePO 中的 isNomalManager 方法,判断管理员状态是否正常。
// =========================================================================
中间是被说明的代码实现
/** ==============================验证申请人是否有权限结束======================= */
说明:
Ø 用于对程序中关键性逻辑的代码进行说明;
Ø 规定用双横线括住,使代码更突出显眼;
2.11、程序修改注释
示例:
//-----------------------------------------------------------------------------------------
/*
case ApplyerTypeConstants.MANAGER ://如果申请人类型为后台管人员
if(!tradePO.isNomalManager(applyer)){//判断管理员状态是否正常
// 传入申请人类型为后台管理员,但传入的申请人却不是管理员或者管理员不可用
result.add(-20032, new Object[] { applyer });
return result;
}
break;
*/
//去掉管理员状态是否正常的判断
//因为管理员是否可用与合同无关,所以在判断时如果是管理员申请的不应该把判断放到 TradePO 中。
//而应该在外部进行判断
// ---- LiuZhenXing 2015-4-24
//-----------------------------------------------------------------------------------------
说明:
Ø 必须对修改代码原因进行说明。
Ø 记录修改人和修改日期。
Ø 整个注释部分用单横线括住,即区分重要注释也能更明确表示代码修改原因。
Ø 注释说明和前边 // 之间两个空格
2.12、Java代码修改时需要修改的地方
Ø 需要将以前的代码注释掉,并且增加修改说明,说明为什么将以前的逻辑改为当前的逻辑,是谁改的,什么时间改的
Ø 要在类注释上增加修改说明,是谁该的,时间,版本号
Ø 将整体的工程版本升级成新的版本
Ø 判断是否需要调整文档 (不要忘记文档索引中的文档版本也要调整)
Ø 完成新代码的编写
3、命名规范
3.1、项目命名
Ø 项目名称:sysname(系统名称) + - + modulename(模块名称)+-+projecttype(工程类型*如果模块下有多个工程时使用)。例如timebargin-core-interface。
Ø 项目名称所有字母必须小写。
Ø 增加任何项目需要由产品经理申报。不可擅自增加工程!
3.2、Jsp命名
Ø 全部小写字母,多个单词之间中线“-”分割。(注:因为数据库和 struts 配置文件中都要配置jsp名称,所以用驼峰法则容易错。)
3.3、Package命名
Ø 根路径为 gnnt.项目版本.<sysname>.<modulename>.<projecttype >(可选)。
Ø 路径中,除二层目录项目版本名称外,全部单词为小写。
Ø 项目版本名称如即将开发的MEBS6。
Ø 单个目录名字不宜过长(最好不超过 15 个字符)。
Ø 在开发中所有功能模块以项目为原子级的。所有子目录不允许放到根目录以外。
Ø 根目录由产品经理级别以上员工订立。
Ø 除本文档中设置的子包外,需建立其他子包的需要产品经理级别以上员工建立。
3.3.1、RMI 实现类路径
Ø RMI 实现类放到根目录的 rmi.impl 路径下
3.4、类命名
类名中的每个单词的首字母均为大写;当有缩写单词时,需要全词大写。类名总体应该是一个名词。具体类名定义规则如下:
3.4.1、接口(Interface)命名
Ø 接口命名为:I + 接口名称。
Ø RMI 接口名为:I + 接口名称 + RMI。
Ø DAO 接口名为:I + 接口名称 + DAO。
3.4.2、接口实现类命名
去掉接口前端 I 作为实现类的名字,如果接口有多个实现类,可在实现类后边增加“_达意单词”进行区分。
3.4.3、抽象类(abstract)命名
A + 类名称。
3.4.4、枚举类(enum)命名
E + 类名称。
3.4.5、线程类(thread)命名
类名称 + Thread
3.4.6、自定义异常类(exception)命名
类名称 + Exception。
3.5、常量命名
示例:
publicstaticfinal String DEFAULT_APPLYER = "admin";
publicstaticfinalbooleanDEFAULT_ISNEEDAUDIT = false;
说明:
Ø 常量名为纯大写,多个单词间用下划线“_”分开。
3.6、变量命名
说明:
Ø 首字母小写,后边单词首字母大写。如果出现简写,则整个简写单词大写。
3.7、方法命名
说明:
Ø 首字母小写,后边单词首字母大写。如果出现简写,则整个简写单词大写。
Ø 方法名总体应该是一个动词。
3.8、公用子包功能归类
3.8.1、po 包
说明:
Ø 存放持久化对象(persistent object)类。(即数据库模型通过映射转化成的持久化类 model)
Ø 命名:表名 + PO。
Ø 表名前缀大写,下划线后第一个字母大写。
Ø 例:表 Ln_Order 对应的 PO 类为 LN_OrderPO。
3.8.2、vo 包
说明:
Ø 存放值对象(value object)类。(通常用于业务层之间的数据传递,和PO一样也是仅仅包含数据。)
Ø 命名:功能名 + VO。
3.8.3、bo 包
说明:
Ø 存放业务对象(business object)类。(在vo、po的基础上进行业务逻辑的加工后产生的业务类。)
Ø 命名:功能名 + BO。
3.8.4、dao 包
说明:
Ø dao(Data Access Object) 是面向对象的数据库访问接口。
Ø 在其 impl 子包中写接口实现类。
3.8.5、thread 包
存放线程类。
3.8.6、exception 包
存放自定义异常类。
3.8.7、util 包
存放工具类。
3.8.8、enums 包
存放系统中的所有枚举类。
3.9、核心子包功能归类
3.9.1、kernel 包
Ø 存放执行核心业务流程。
Ø 本包下可分为transaction和非transaction目录。transaction目录下的业务功能是配置了事务的,非transaction目录没有配置事务。
Ø 在各自 impl 子包中存放实现类。
Ø 本包中的类为主要实现核心逻辑业务功能类,以供外部调用。
3.9.2、sysscheduler 包
存放系统状态控制等系统调度相关类。
3.9.3、rmi 包
存放对外开放的 RMI 服务接口,在其 impl 子包中存放实现类。
3.10、Web服务子包功能归类
3.10.1、action 包
存放 web 访问的相应 Action 类。
3.10.2、service 包
存放 Web 服务调用的数据库访问事务层。
4、编程书写规范
4.1、缩进
Ø 子代码快为父代码快基础上向后移1个 tab 键
Ø 设定一个 tab 键为 4 个字符
4.2、列宽
Java代码的每行列宽不超过 100 。
4.3、类(class)内容顺序规范
Ø 类的编码顺序为:先是属性,然后是构造方法,再然后是业务方法,最后是属性的 get*** 和 set*** 方法。
Ø 当同一个类中有多个方法处理一个对象时,将其放到一起,并按一定的顺序书写;如:在 DAO 中对一个表的增、删、改、查应该放到一起。顺序为:先写添加方法(add***)、然后是修改方法(update***)、然后是查询方法,查询方法先写按主键查询(get***ByID)、然后写删除方法(del***)。
4.4、其它代码书写要求
Ø 所有 java 代码不允许有警告标志。
Ø 不允许以import java.util.*类似的方式导入,而要明确的import具体类。
Ø 不允许import 从本类中未用到的类,造成资源浪费。
Ø 每个 Java 文件建议不超过 2000 行代码,超过了,建议拆分。
Ø 每个 Java 类建议不超过 150 个方法。
Ø 单行字符数不超过 100 个。
Ø 方法建议不超过 7 个参数,超过的建议封装成对象。尤其是对外 RMI 接口,建议全部封装成对象,以方便扩展。
Ø 非必要情况,代码中不要出现 System.exit(); 方法。
Ø 打印日志,建议少用 System.out.print 方法,最后提交时不允许出现 System.out.print。
Ø switch 块最后一定要有 default。
Ø try catch 块最后的 Exception 中非特殊情况不能为空。
Ø if 最大深度为 4,当if 深度超过 4 建议拆分成多个方法执行。
Ø try catch 最大深度为 3。
Ø 判断变量和值相等要用“abc”.equale(A),格式,不要用 A.equale(”abc”),防止空指针异常。
Ø 不建议类似的代码风格,不具有可读性:
String b = (a==null || a.length<1) ? null : a.substring(1)。
Ø 禁止直接使用类似于-1, 0, 1, and 2这样的魔法数字,可读性为0。将魔法数字定义为常量。
Ø 数据库中的 char 类型列在 java 代码中不要用 char或Character 类型,全部用 String 类型属性对应;数据库中的 number 类型列在 java 中也不要用 float 类型,全部用 double、int、short 类型属性对应。
5、编程逻辑规范
5.1、把常量放到前边
Ø 当执行字符串比较方法(equals)等时,将字符串放到前边,防止空指针异常产生。
例如:
if(variable.equals(‘literal’)){……}
应改为
if(“literal”.equals(variable)){……}
Ø 当使用 > 、< 、== 、!= 等进行数字判断时,先要判断数字是否为空。
例如:
if( num == 5){……} 或 if(5 == num) {……}
当 num 为 null 时都会有空指针异常。
应在比较前判断空
if(num != null && num == 5){……}
5.2、不要相信 -1
Ø 不要相信 Javadoc 中的 特殊值判断描述。
例如:
字符串的 indexOf 方法,doc 中的描述为:字符在字符串中第一次出现的位置将作为结果被返回,如果字符串不存在,则返回 -1。
if(string.indexOf(character) != -1){……}
但很多场景下,如过不区分大小写的话,jdk升级后可能会返回 -2 呢?
所以以上代码应改为
if(string.indexOf(character) >= 0){……}
5.3、检查null和长度
Ø 不管什么时候,你有一个集合、数组、或其他的对象,在使用时,先要判断对象不为空,防止空指针异常。
例如:
if(array.length > 0) {……}
应该写成
if(array != null && array.length > 0) {……}
5.4、用大括号括住 switch 的每一个 case 块
Ø 为了程序的易读性和正确性,程序清晰非常重要,而每个 case 的代码快用大括号括住,则程序更清晰。
例如:
switch(value) {
case 1 : int j=1;break;
case 2 : int j=2;break;
default : int j=3;
}
应该写成
switch(value) {
case 1 : {
int j=1;
break;
}
case 2 : {
int j=2;
break;
}
default : {
int j=3;
}
}
5.5、判断与业务顺序规范
Ø 当方法中有多个判断和多个业务查询或操作时,要先进行判断,当判断不满足时返回,全部判断完成后再进行业务操作,防止资源浪费。
5.6、判断思想规范
Ø 一般代码逻辑最好是判断否,当否时,直接返回失败,防止复杂逻辑里的判断嵌套过多影响程序易读性。
例如:
/**获取用户信息*/
public UserPO getUser(String userID) {
UserPO result = null;
if(userID != null) {//如果用户编号不为空
result = dao.getUser(userID);
if(result.getStatus() != 1){//如果用户状态不为正常状态
result = null;
}
}
return result;
}
以上代码最好改写成
public UserPO getUser(String userID) {
//判断空
if(userID == null) {
return null;
}
//获取用户信息
UserPO result = dao.getUser(userID);
//判断用户是否为空
If(result == null || result.getStatus() == null || result.getStatus() != 1){
return null;
}
return result;
}
6. 添加模版
为使得代码统一化,且书写方便,使用的开发工具(MyEclips)需要引入以下两个模版:
代码模版
Window——>preferences——> Java——>Code Style——>Code Templates,点击 Import . . .——>查找到金网安泰代码模版.xml——>点击 Apply——>勾选上下边的复选框,然后点击 OK。
格式化模版
Window——>preferences——>Java——>Code Style——>Formatter——>Import…——>查找到金网安泰格式化模版.xml——>点击 Apply,然后点击 OK。
log日志快捷键模版
Windows->Preferences->Java->Editor->Import... ——>金网安泰log日志快捷键模版.xml——>点击 Apply,然后点击 OK。
参考:北京金网安泰信息技术有限公司 公开 Java编码规范文档