JAVA编码规范
变更履历
时间 | 版本 | 变更人 | 变更内容 |
2008-8-3 | 1.0 | 裴晓锋 | 新规 |
2008-8-13 | 1.1 | 裴晓锋 | 按照会议080807内容修改 |
2008-8-23 | 1.2 | 裴晓锋 | 按照会议080807和080820内容修改 |
2008-10-22 | 1.3 | 裴晓锋 | 按照会议080902内容修改 |
Review履历
时间 | 版本 | Review人 | 指正内容 |
2008-8-6 | 1.0 | 张鹏 |
|
2008-8-14 | 1.1 | 张鹏 |
|
2008-8-24 | 1.2 | 张鹏 |
|
1.
目录
5.6 不要在Page, Form类中直接执行SQL语句的处理
1 目的
想要保证代码的质量,在开发过程中没有或不遵循开发规范会造成严重的后果。为此,拟定了此开发规范。希望大家在开发过程中予以重视。
2 命名规范
命名包括包的命名、类的命名、方法命名、变量命名。
一般要遵循以下规则:
1. 规则1: 命名应明确
一般情况下不使用添加1、2、3等数字进行命名, 如item1, item2, item3
2. 规则2:命名应使用专业名词
如果开发的项目所在的领域已经存在很好的专业术语,请使用它们。在CRM软件中,顾客customer, 安案件sales, 社员employee, 报告report, 计划schedule, 产品product, 进程progress, 活动action, 分析analysis都是专业的术语。
2.1 Package的命名
Package的名字应该都是由一些小写单词组成。
例:com.digitalchina.info.appframework.extjs.servlet;
2.2 Class的命名
Class的名字必须由大写字母开头而其他字母都小写的名词组成
例:public class ComboDataAction
2.3 变量的命名
变量的名字必须用一个小写字母开头,后面的单词用大写字母开头。
例: Map requestParams = request.getParameterMap
注:变量的命名根据其作用域的不同而有所区别,作用域越大,应越有意义。
2.3.1 Class级变量
对class变量命名采用 自解释性名称
例: public class CustomizeInfoRetrieve {
protected String[] strPostNames
private boolean cookieIsAdmin
private HtmlPage page
…
}
2.3.2函数级变量(推荐)
对一般变量名采用 数据类型缩写+名称(变量名尽量具有自解释性)
[推荐]见变量名缩写表
例: public String[] getUserInput(int idx) {
String[] strGroupName
int nCustomerID
…
…
}
注:
对于对象变量命名采用 以被引用对象的相关名称命名,并使其具有自解释性
例: private Vector employeeVector_ = new Vector();
License license = new License();
2.3.3 Static Final变量的命名
Static Final变量的名字应该都大写,并且指出完整含义。
例:static final String KEY_MEMBER = “member”;
2.3.4参数的命名
使用有意义的参数命名,如果可能的话,使用与赋值的字段一样的名字:
setCounter(int size){
this.size = size;
}
注:方法的参数不推荐使用Vector类型的变量,因为方法的使用者不知道Vector中包装的对象的类型,应该使用数组。
2.3.5数组的命名
数组应该总是用下面的方式来命名:
byte[] buffer;
而不是:
byte buffer[];
2.4 方法的命名
方法命名必须用一个小写字母开头,后面的单词用大写字母开头。
例: private boolean insertProgress(int nCode)
public void print(PrintWriter pw)
对一般属性的取值,赋值采用get/set模式
例: prublic void getParameters()
prublic void setParameters(String strName)
对具有Boolean值的属性使用is/set命名模式
例: public boolean isRunning()
public void setRunning(boolean bRunning)
把值加入到对象中使用load, put
例: public loadCorpExtInfo()
变换类型to
例: public toInt()
静态工厂valueOf
例:public static Boolean valueOf(boolean b) {
return (b ? Boolean.TRUE : Boolean.FALSE);
}
动词 | 使用方法 |
get | 返回值。 |
set | 设定値(覆盖)。 |
is | 判断返回boolean |
load | 读取object |
insert | 将値读入object |
delete | 删除object |
update | 升级object |
search | 查找 |
put | 追加値(向列表等里面) |
| 向指定的stream里输出 |
to | 变换类型 |
3 Java文件样式
3.1 数据、方法书写顺序
3. 类的实体按照以下顺序书写:
1、 数据(推荐),按以下顺序排列,在访问控制符变化时加入一行空行。
2、 final变量,final变量放在最前面,按照静态、非静态,访问控制符由public到private的顺序进行排列。
3、 静态变量
1) public
2) protected
3) private
4、 实例变量
1) public
2) protected
3) private
5、 方法,所有java文件注释最好用英文,尽量不要用中文
3.2 Package/Imports
package行要在import行之前,import中标准的包名要在本地的包名之前,而且按照字母顺序排列。
Java标准包放在最前,其它包放在后面。[推荐]不同意义的import进行分段。
如果import行中包含了同一个包中的不同类,则可以用 * 来处理。
package hotlava.net.stats;
import java.io.*;
import java.util.Observable;
import hotlava.util.Application;
这里java.io.* 使用来代替InputStream和OutputStream的。
3.3 Class
接下来的是类的注释,一般是用来解释类的。
/**
* This class is the conntroller in the MVC structure.
* @author Dezhi Sun
* @version 1.00, 04/29/02
* @since 3.5
*/
接下来是类定义,[推荐]extends与implements分行描述
public class CounterSet extends Observable
implements Cloneable
推荐类名称举例:
Suffix | Class分类 |
**Table | 管理数据库的表 |
**Query | 管理数据库的讨论 |
**Rec | 管理从数据库读取的数据 |
**Page | 控制页面的动作 |
**Form | 控制显示内容 |
**Stat | 现在的状态 (控制用户操作等从DB读取的信息以外的问题) |
3.4 类的成员变量
接下来是类的成员变量,一般是用来解释成员变量的:
/**
* Packet counters
*/
public int[] packets_;
public的成员变量可以加上文档(JavaDoc)。
3.5 存取方法
接下来是类变量存取的方法。它只是简单的用来将类的变量赋值获取值
/**
* Get the counters
* @return an array containing the statistical data. This array has been
* freshly allocated and can be modified by the caller.
*/
public int[] getPackets() { return copyArray(packets, offset); }
public int[] getBytes() { return copyArray(bytes, offset); }
public int[] getPackets() { return packets_; }
public void setPackets(int[] packets) { this.packets_ = packets; }
3.6 构造函数
接下来是构造函数,它应该用参数递增的方式写(比如:参数多的写在后面)。
public CounterSet(int size){
this.size = size;
}
3.7类方法
接下来下面开始写类的方法:
对类方法注释要紧靠在每个方法的前面,如下
/**
* 类方法注释
*/
关于方法注释应该象书写专业文档一样,它应该包括以下内容:
1. 方法的用途,这是类注释必须有的。
2. 如果方法包括的逻辑比较重要或者复杂, 不仅要对这个方法用来做什么进行说明,还应该说明为什么这样做,这对于版本维护特别重要,别的开发人员会清楚明白你的方法是不是需要修改。
3. 访问控制的说明,一个方法的访问控制定义应该明确,应当有充分的理由这样做,有的开发人员不重视这一点,或者全部是public,或者全部是private,只要求能编译通过并完成现有功能。这样做会为后续开发带来问题。
4. 下面是javadoc必须的注释:
1. @param该标记在当前方法的“prameter”一节中增添一个条目,具体的描述可以跨越多行,可使用HTML标记,要求所有的 @param要连在一起。
2. @return该标记在当前方法的一个“Returns”小节。具体的描述可以跨越多行,可使用HTML标记。
3. @throws该标记在当前方法的“Throws”一节中增添一个条目JAVADOC会自动建立一个超连接让用描述可以跨越多行,可使用HTML标记,要求所有的 @throws要连在一起。
4. @see该标记在“See also”小节中增加一个超链接使其可以转到JAVADOC文档的其他部分或转到一个外部文档。具体后面可以采用以下几种模式之一即可:
a) package.class#method XXXXXX //此户理解所抛出的异常,具体的形式最常用,省略包名表示在同一包
b) <a href=”HTTP://WWW.SINA.COM.INDEX.HTML”>XXXXX</a>
c) ”文字”
/**
* <b> Set the packet counters</b>
5. * (such as when restoring from a database)
6. * @param r1 input vector store message 1
* @param r2 input vector store message 2
* @param r3 input vector store message 3
7. * @param r4 input vector store message 4
8. * @return null
* @throws if parameteter1 is error throw IllegalArgumentException
* @see jp.co.softbrain.wes.sfa.admin.AdminMenuPage#print()
*/
protected final void setArray(int[] r1, int[] r2, int[] r3, int[] r4)
throws IllegalArgumentException {
//
// Ensure the arrays are of equal size
//
if (r1.length != r2.length || r1.length != r3.length || r1.length != r4.length)
throw new IllegalArgumentException(”Arrays must be of the same size”);
System.arraycopy(r1, 0, r3, 0, r1.length);
System.arraycopy(r2, 0, r4, 0, r1.length);
}
推荐做法: 写代码前写注释,这样给你一个机会在写代码前去想清楚代码怎样书写,并能确保代码注释的书写。 |
3.8 (推荐)方法内注释
9. 对涉及到流程分支较多或算法比较复杂的程序段,建议在此之前对流程或算法进行适当的解释。
10. 对某些关键性的程序行,建议在其前一行或行尾加以注释。
11. 我们书写代码最好的结果是用代码来说话,也就是如果代码写的好,并不需要太多的注释。但是有这样的方法,它们非常复杂不是每个程序员都有能力来看懂,来重写,这样就必须书写详细的注释。
12. /* Check if cookieDepart is available;
13. * If the administration login is needed, we should set the adminstration group code;
*/
boolean bForceAdmin = false;
if(session != null) {
Boolean Bool = (Boolean)session.getValue(“force_admin”);
if(Bool != null )
bForceAdmin = Bool.booleanValue();
}
if(!bForceAdmin&&cookieDepart_.equals(“”)==false)
//get department’s Code
14. departCode_ = Integer.parseInt(cookieDepart_);
System.out.println(cookieDepart_);
int np = departVector_.size();
boolean bEnter = false;
// Useless cookie’s interferance should be avoild;
for (int i=0; i<np; i++) {
15. Depart p = (Depart)departVector_.elementAt(i);
16. if (p.getCode() == departCode_) {
17. bEnter = true;
}
}
… …
18.
4 代码编写格式
4.1 文档化
必须用javadoc来为类生成文档。不仅因为它是标准,这也是被各种java编译器都认可的方法。
4.2 缩进
缩进应该是每行4个空格。
4.3页宽
页宽应该设置为80字符. 源代码一般不超过这个宽度, 超长的语句应该在一个逗号或者一个操作符后折行. 一条语句折行后, 应该比原来的语句再缩进4个字符.
4.4 {} 对
要求{}对排列整齐,最好上下一致,原则上要求便于阅读和配对
简单的可写成一行
例:if (i < 0) { i ++ };
一般情况可用以下格式
if (i < 0) { //{与条件语句在同一行
i ++
};
4.5 空白行
19. 空白行应该以下情况中出现:
l 类之间
l 类方法之间
l 最后一个类变量声明和第一个类方法之间
l 在第一行注释之前
l 语句中完成相似的功能模块之后
20. 一般情况下,应该只空一行。
4.6 括号
左括号和后一个字符之间不应该出现空格, 同样, 右括号和前一个字符之间也不应该出现空格. 下面的例子说明括号和空格的错误及正确使用:
CallProc( AParameter ); // 错误
CallProc(AParameter); // 正确
不要在语句中使用无意义的括号. 括号只应该为达到某种目的而出现在源代码中。下面的例子说明错误和正确的用法:
if ((I) = 42) { // 错误 - 括号毫无意义
if (I == 42) or (J == 42) then // 正确 - 的确需要括号
5 页面注意事项
5.1 Html类的html标签统一采用小写。(推荐)
举例说明:
public void print(PrintWriter pw) throws Exception {
printWAPHeadDTD(pw,ConstantValue.ENCODE_CN);
pw.println(“<head>”);
printTitleTag(pw);
pw.println(“</head><body><p>”);
pw.println(throwable.getMessage());
pw.print(“</p><hr/><p>”);
pw.println(“hello”);
printAccesskeyTag(pw,1,request.getRequestURI(),”返回首页”,true,getBPType()); pw.print(“</p><hr/>”);
pw.print(“</body>”);
pw.print(“</html>”);
}
5.2采用符合XML格式规范的html。(推荐)
每个html页面的开始标签都应该有相应的结束标签。
例子同上。
5.3 字符集变换
在MultipartRequest内部一度进行了StringFilter.AreWoSore。因此,在Pageclass等中不再进行StringFilter.AreWoSore。
5.4 登录与变更画面
对于Page类,其关于登录画面用(****EntryPage),关于变更画面用(****ModifyPage)也就是分别生成两个class。对于Form,如果登录和变更画面元素基本相同的时候,登录和変更两者使用一个类(****EntryForm)。
5.5 不要新建继承PageBase的Page类
在旧的source file中,存在PageBase的继承类,但原则上在今后新生成的页面,不继承PageBase类,都是继承HtmlForm类。
5.6 不要在Page, Form类中直接执行SQL语句的处理
Page和Form分别是逻辑类和页面展示类,应该在Page和Form中调用Action类来获取数据,不要直接执行SQL、或调用Query类等。
5.7 sendRedirect方法
以MultipartRequest发送了request时,有再次被发送的可能性,因此一定要使用HtmlPage的sendRedirect方法。
5.8 reload时的処理
对输入form进行了reload时,原则上text, checkbox等所有的输入form中被输入的信息都被沿用到reload后的画面中。
5.9 父窗口的reload
打开窗口,在其中进行登录或更新处理时,如果对窗口的opener(原来的父窗口)有影响时,一定要让父窗口进行reload。另外,在JavaScript中执行父窗口的JavaScript函数时,确认窗口没有被关闭、父画面没有转换、要执行的函数存在。
例} if (opener != null && !opener.closed && opener.reload)
5.10 提交按钮后要设置成disable
如果用户多次按提交按钮,在登录页面还没有关闭之前,这样就提交了多次,出现了登录了多条的结果。遇到这种情况的时候,应使登录按钮的状态变为不可使用状态(document.form.submitbutton.disabled=true.),这样就防止了多次提交。但要注意两点:
1. 在对其它一些条件判断失败的情况,不能使登录按钮变成不可使用状态(例如,在登录TODO的时候,没有输入ToDo名,这时会给出提示信息,要求输入ToDo名)。
2. 对回车键的支持,在登录一些表单时,一般会支持按回车键就提交的功能,为了支持这种功能,登录按钮是submit类型的按钮,这样一来,使登录按钮变成不可使用的代码需要放在onSubmit事件中,或者在这个事件中调用这个代码。
5.11 页面中必需项文本框中不允许全部输入半角或全角空格
针对用户必需输入的文本框内容,在提交时进行判断,如果全为空,则提示用户必须输入内容,如果不全为空,则进行trim处理,截掉开头和结尾的空格后再进行提交。
以下的javascript function可以做trim处理,请大家参照:
function trim(str){
var strResult;
var charTemp;
var i;
strResult = "";
for ( i = 0; i < str.length; i++ ){
charTemp = str.charAt(i);
if ( charTemp != " " && charTemp !=" "){
strResult = str.substring(i);break;}}
for ( i = strResult.length-1; i >= 0; i-- ){
charTemp = strResult.charAt(i);
if ( charTemp != " " && charTemp !=" "){
strResult = strResult.substring(0,i+1);break;}}
return strResult;
}
6 关于数据库编程的注意事项
6.1 关于多数据库的注意事项:
6.1.1 不使用to_char等的SQL函数
SQL函数依存于不同的DBMS ,所以不使用。时间的指定、取得使用setTimestamp / getTimestamp方法。
6.1.2 Blob数据统一进行处理
Blob数据用Table类定义、用readBlob writeBlob方法访问。(作为多个DBMS对应)
7 修改他人代码是的注意事项
7.1 在他人代码中增加代码的时候
当需要在他人代码中增加自己的代码的时候,请按照如下规则增加相应注释:
//Add By XXX(英文名字) For XXXXX(功能或模块,最好用英文) In YYYY-MM-DD Begin
……(所增加的代码)
//Add By XXX(英文名字) For XXXXX(功能或模块,最好用英文) In YYYY-MM-DD End
7.2 修改他人代码的时候
当需要修改他人写的代码的的时候,请按照如下规则增加注释并修改代码:
//Modify By XXX(英文名字) For XXXXX(功能或模块,最好用英文) In YYYY-MM-DD Begin
/*
*………(他人写的变更前的代码)
*/
…………(变更后的代码)
//Modify By XXX(英文名字) For XXXXX(功能或模块,最好用英文) In YYYY-MM-DD End
7.3删除他人代码的时候
当需要删除他人写的代码的时候 ,请按照如下规则删除并增加注释:
//Remove By XXX(英文名字) For XXXXX(功能或模块,最好用英文) In YYYY-MM-DD Begin
/*
* …………(要删除的代码)
*/
//Remove By XXX(英文名字) For XXXXX(功能或模块,最好用英文) In YYYY-MM-DD End
7 其他注意事项
7.1 io流应该手动关闭
举例:fileInputStream.close();
7.2 近可能使类的成员属性和方法的可访问能力最小化
7.3使用StringBuffer对象
在处理String的时候要尽量使用StringBuffer类,StringBuffer类是构成String类的基础。当我们在构造字符串的时候,我们应该用StringBuffer来实现大部分的工作,当工作完成后将StringBuffer对象再转换为需要的String对象。比如:如果有一个字符串必须不断地在其后添加许多字符来完成构造,那么我们应该使用StringBuffer对象的append() 方法。如果我们用String对象代替StringBuffer对象的话,会花费许多不必要的创建和释放对象的CPU时间。
7.4避免太多的使用synchronized关键字
避免不必要的使用关键字synchronized,应该在必要的时候再使用她,这是一个避免死锁的好方法。
7.5可以使用static变量仅限以下的情况
1. 定常量值,例如public static final AGE = 12;
2. 向AP server中进行buffering的値。不过,在e-sales manager中,由于存在多个properties file的切换而进行的运用单位的切换功能,利用static变量直接保管信息的的情况几乎不会有。
3. 使用DBMS在不需要在一个servlet container上使用不同的DBMS的前提下,作为static変数保持。
7.6 if、for等操作的注意事项
7.6.1 if, for语句的逻辑不要太深。
这样可读性会非常低,因此请尽量控制在3~4重nest左右。 还需要更深时,另外作为一个private method分开処理。
7.6.2 在loop中不要变更loop変数値
例)为了避免以后发生混乱,在ESM中不可以进行如下这样地实装。
7.5.3 for(int i=1; i<10; i++){
if(xxxx) i--;
7.7开发结束时,一定要将不被使用了的方法和变量删除
7.8处理INTEGER型时,一定要使用long型。
在int型中达到10位以上时则无法処理。使用long型。
7.9 session管理
session信息全部用LoginUser class进行管理。其他的地方不能管理session信息。
附录:
变量名缩写表
数据类型 | 缩写 | 注释 |
int | n |
|
short | s |
|
long | l |
|
byte | b |
|
float | f |
|
double | d |
|
char | c |
|
boolean | bln |
|
String | str |
|
Vector | vect/v |
|
Map | map |
|
Object | obj/o |
|
InputStream/OutputStream | in/out |
|