代码审查参考文档.docx

上传人:b****2 文档编号:17356320 上传时间:2023-07-24 格式:DOCX 页数:17 大小:22.79KB
下载 相关 举报
代码审查参考文档.docx_第1页
第1页 / 共17页
代码审查参考文档.docx_第2页
第2页 / 共17页
代码审查参考文档.docx_第3页
第3页 / 共17页
代码审查参考文档.docx_第4页
第4页 / 共17页
代码审查参考文档.docx_第5页
第5页 / 共17页
代码审查参考文档.docx_第6页
第6页 / 共17页
代码审查参考文档.docx_第7页
第7页 / 共17页
代码审查参考文档.docx_第8页
第8页 / 共17页
代码审查参考文档.docx_第9页
第9页 / 共17页
代码审查参考文档.docx_第10页
第10页 / 共17页
代码审查参考文档.docx_第11页
第11页 / 共17页
代码审查参考文档.docx_第12页
第12页 / 共17页
代码审查参考文档.docx_第13页
第13页 / 共17页
代码审查参考文档.docx_第14页
第14页 / 共17页
代码审查参考文档.docx_第15页
第15页 / 共17页
代码审查参考文档.docx_第16页
第16页 / 共17页
代码审查参考文档.docx_第17页
第17页 / 共17页
亲,该文档总共17页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

代码审查参考文档.docx

《代码审查参考文档.docx》由会员分享,可在线阅读,更多相关《代码审查参考文档.docx(17页珍藏版)》请在冰点文库上搜索。

代码审查参考文档.docx

代码审查参考文档

 

代码审查参考文档

代码审查参考文档

代码审查〔codereview〕是保证软件质量的一个重要环节,通过审查代码能够发觉代码中可能存在的问题并给予纠正,这些问题可能包括设计上的、实现上的或者编程风格等多方面。

本文档通过列举代码编写过程中的一些常见的细节问题,为代码审查环节提供参考。

Java代码

一、对象和变量

1.存在未被使用的变量

Eclipse会自动用下划线标出

2.对象的重复创建

这是系统中普遍存在的问题,比如:

publicclassPrtGrpEndorsementBL{

privateGlobalInputmGlobalInput=newGlobalInput();

privatebooleangetInputData(VDatacInputData){

mGlobalInput=(GlobalInput)cInputData.getObjectByObjectName(

"GlobalInput",0);

returntrue;

}

}

那个地点mGlobalInput对象属于重复创建,因为在getInputData方法里会对它进行赋值,mGlobalInput使用的应该是从jsp页面传入的对象,因此改为privateGlobalInputmGlobalInput=null;

又如:

Stringmsg="";

if(..){

msg="A";

}

else{

msg="B";

}

那个地点msg同样属于重复创建,改为Stringmsg=null;

3.变量的作用域

Java的局部变量能够定义在函数的任何位置,有部分由c转学java的程序员适应将变量都定义在函数的顶部,因为在c里只能那样定义。

但实际上变量的作用域越短程序的内聚性就越高,耦合性也更低,程序更容易明白得,因此在java里应该在使用前才定义变量。

4.局部变量的危害

定义过多的不必要的局部变量是造成系统难以爱护的缘故之一,因为每增加一个局部变量我们就要先化时刻去明白得那个局部变量的意思,因此我们要减少局部变量的使用。

用函数的返回值来替代局部变量是一种有效的方法,这就需要我们用重构的方式从大的函数中提出小的函数,用小函数的返回值来替代原有的局部变量。

把大函数分解本身也能够降低程序的耦合度。

二、常量

1.硬编码,将代码写死

比较严峻的情形是将险种代码写死在程序中,比如:

if("21301".equals(tLCGrpPolSchema.getRiskCode())){

...

}

那个地点将〝21301〞写死会为系统的爱护和升级带来专门大困难,最好是以险种定义的形式去动态猎取险种代码。

假如实在描述有困难,能够考虑建立一个Constants常量类,将其以常量的形式定义,如此以后爱护只需改动一处即可。

业务系统中存在许多的状态标志,比如AppFlag的状态〞0〞为未签单,〞1〞为已签单,〞2〞为保全增人。

尽管这些状态的意义日后已没有改动的可能,但直截了当将〞0〞、〞1〞、〞2〞写在程序里会导致程序可读性差,因此我们同样能够将其定义在常量类中,并加上注释,如此我们不必每次都去翻看文档或pdm,看常量类里的注释就行了。

2.禁用常量接口

Java的常量有一种不行的用法,确实是将常量定义在Inferface中,这种方使定义的常量在使用时能够省去类名前缀,但会为以后的爱护带来苦恼。

因此不要试图用继承的方式去使用常量。

三、数组

1.数组的定义

数组的定义采纳String[]str=null;的形式,而不是Stringstr[]=null;

2.数组越界

数组越界是比较常见的错误,比如:

privatevoiddealCont(){

LCContDBtLCContDB=newLCContDB();

tLCContDB.setGrpContNo(mGrpContNo);

tLCContSet=tLCContDB.query();

for(inti=0;i

LCContSchematLCContSchema=tLCContSet.get(i);

LCPolDBtLCPolDB=newLCPolDB();

tLCPolDB.setContNo(tLCContSchema.getContNo());

LCPolSettLCPolSet=tLCPolDB.query();

for(intj=0;j

LCPolSchematLCPolSchema=tLCPolSchema.get(i);

...

}

}

}

上面的程序就会产生数组越界,应该把tLCPolSchema.get(i);中的i改为j

数组越界的错误在编译时可不能报错,直到程序运行时才会被发觉。

有一种方法能够幸免此bug的产生,程序改为:

privatevoiddealCont(){

LCContDBtLCContDB=newLCContDB();

tLCContDB.setGrpContNo(mGrpContNo);

tLCContSet=tLCContDB.query();

for(inti=0;i

dealPol(tLCContSet.get(i));

}

}

privatevoiddealPol(LCContSchematLCContSchema){

LCPolDBtLCPolDB=newLCPolDB();

tLCPolDB.setContNo(tLCContSchema.getContNo());

LCPolSettLCPolSet=tLCPolDB.query();

for(inti=0;i

LCPolSchematLCPolSchema=tLCPolSchema.get(i);

...

}

}

把内层循环提到一个函数中,在编译时就幸免了bug的产生,而且降低了循环的层次,具有更好的可读性。

四、静态方法

1.静态方法的错误使用

com..lis.bq.ChangeCodeBL是保全中的一个工具类,它用两个重载了的静态方法getCodeName,它封装了CodeQueryBL类,其作用是依照code和codeType得到codeName,

然而在使用过程中却显现了如下程序:

ChangeCodeBLtChangeCode=newChangeCodeBL();

strArr[0]="与被保人关系原为"+tChangeCode.getCodeName("relation",

tLCBnfSchema.getRelationToInsured());

明显,作为静态方法getCodeName不应该如此使用,应改为:

strArr[0]="与被保人关系原为"+ChangeCodeBL.getCodeName("relation",

tLCBnfSchema.getRelationToInsured());

之因此显现如此的错误是因为ChangeCodeBL在定义构造函数时出了问题,构造函数的类型写成了public,但那个类实际上是不承诺创建对象的。

因此改为privateChangeCodeBL(){}就能够从全然上幸免错误的产生。

静态方法的另一个常见的应用是用来创建对象,比如工厂模式和单例模式,这时我们同样应该使用pritvate的构造函数。

五、参数

1.对参数进行校验

关于传入类的参数,我们需要对其合法性进行校验,比如:

privatebooleangetInputData(VDatacInputData){

LCGrpContSchematLCGrpContSchema=

(LCGrpContSchema)cInputData.getObjectByObjectName(

"LCGrpContSchema",0);

LCContDBtLCContDB=newLCContDB();

tLCContDB.setGrpContNo(mGrpContSchema.getGrpContNo());

mLCContSet=tLCContDB.query();

returntrue;

}

这段代码是存在风险的,因为我们并不能保证getObjectByObjectName一定会得到一个对象返回,假如"LCGrpContSchema"写错了或者jsp页面全然就没有创建LCGrpContSchema对象,那么tLCGrpContSchema就为null,执行到mGrpContSchema.getGrpContNo()时就会抛出空指针专门。

因此改为:

privatebooleangetInputData(VDatacInputData){

LCGrpContSchematLCGrpContSchema=

(LCGrpContSchema)cInputData.getObjectByObjectName(

"LCGrpContSchema",0);

if(tLCGrpContSchema==null){

...

returnfalse;

}

LCContDBtLCContDB=newLCContDB();

tLCContDB.setGrpContNo(mGrpContSchema.getGrpContNo());

mLCContSet=tLCContDB.query();

returntrue;

}

关于public的方法,我们同样需要对其参数进行校验,因为我们不能保证客户程序的调用是正确的。

2.不对参数进行赋值操作

Java中只有int,long,double等差不多类型的参数是使用值传递,而对象参数差不多上传的地址,因此在函数中对对象参数赋值是无效的。

C语言中由于有指针能够对函数外的对象的值直截了当进行修改,但java不行,因此参数只作为数据的传入而不能作为数据的传出。

六、代码简化

1.除去余外的代码

比如:

LCContDBtLCContDB=newLCContDB();

tLCContDB.setGrpContNo(tGrpContNo);

LCContSettLCContSet=tLCContDB.query();

if((tLCContSet==null)||(tLCContSet.size()==0)){

returnfalse;

}

那个地点tLCContSet==null的判定是余外的,因为tLCContDB.query()确信会创建一个对象返回,不存在返回null的情形,改为if(tLCContSet.size()==0)

又如:

if(tLCPolSet.size()>0){

for(inti=1;i<=tLCPolSet.size();i++){

tLPEdorItemSchema.setPolNo(tLCPolSet.get(i).getPolNo());

if(this.queryLPPol(tLPEdorItemSchema)){

tLPPolSet.add(this.getSchema());

}

}

}

那个地点if(tLCPolSet.size()>0)判定余外,将其除去

2.合并重复的代码

比如:

if(getMoney>0){

strArr[0]="本次申请应补交保费"+

setPrecision(Math.abs(getMoney),2)+"元。

";

tlistTable.add(strArr);

}

else{

strArr[0]="本次申请应退还保费"+

setPrecision(Math.abs(getMoney),2)+"元。

";

tlistTable.add(strArr);

}

那个地点tlistTable.add(strArr);重复,将其提到if别处合并。

七、条件逻辑操纵

1.去除操纵标志

受结构化程序设计的阻碍,有人喜爱用boolean型的标志来操纵程序的流程,比如:

privatebooleancheckMoney(){

booleanflag=true;

if(money>0){

flag=true;

}

else{

flag=false;

}

returnflag;

}

但逻辑标志的使用会降低程序的可读性,当逻辑复杂时修改爱护会比较困难,因此改为:

privatebooleancheckMoney(){

if(money>0){

returntrue;

}

else{

returnflase;

}

}

或者:

privatebooleancheckMonay(){

if(money>0){

returntrue;

}

returnflase;

}

2.位运算符

&和|差不多上位运算符,但它们也可在if语句中进行条件判定,但现在仍是执行的位运算。

因为使用&和|容易引起不必要的错误,因此禁止在条件判定中使用位运算符。

八、专门处理

1.try{}catch{}语句的滥用

java的专门处理机制能够把错误处理程序和正常程序分开,使我们能够更容易进行复杂的错误处理。

但专门处理机制容易被滥用,因为大多数的java书只是描述了专门机制的语法,而并没有说明什么情形下使用专门。

专门之因此是专门是因为其不可预料性,比如我们通过io读取配置文件而文件并不存在,又比如通过jdbc读取数据库服务器却当掉了,这些差不多上需要我们通过专门机制来处理的情形。

而下面的代码对专门的处理是错误的:

privatebooleangetInputData(VDatacInputData){

try{

LCGrpContSchematLCGrpContSchema=

(LCGrpContSchema)cInputData.getObjectByObjectName(

"LCGrpContSchema",0);

}

catch(Exceptione){

CError.buildErr(this,"接收数据失败");

returnfalse;

}

returntrue;

}

因为我们只要看一下getObjectByObjectName的代码就明白那个函数假如处理错误会返回一个null而不是抛出专门,那个地点的try{}catch{}是没有任何意义的,而且会降低程序的性能,因为对专门的捕捉会花去额外的开销。

2.空指针专门

NullPointerException是比较常见的错误,因此我们在使用一个对象前一定要确保那个对象差不多被创建过了。

3.数据格式化专门

使用Integer.parseInt(Strings)进行数据格式转化时假如数据格式错误会抛出NumberFormatException专门,那个专门继承于RuntimeException,是一个非强制捕捉的专门。

但从程序的健壮性考虑,如下代码我们最好能进行专门处理:

将intbnfGrade=Integer.parseInt(tLPBnfSchema.getBnfGrade());改为:

try{

intbnfGrade=Integer.parseInt(tLPBnfSchema.getBnfGrade());

}

catch(NumberFormatExceptione){

...

}

因为那个地点我们并不能保证tLPBnfSchema.getBnfGrade()返回的一定是能够被格式化为整数的字符串。

假如我们有足够的理由确保那个地点可不能产生专门,那么专门处理也可省略,正如前面所说的,专门处理的是不可预料的错误。

4.数组越界

数组越界专门同样容易被滥用,比如:

try{

inti=0;

while(true){

a[i++].f();

}

}

catch(ArrayIndexOutOfBoundsExceptione){

...

}

这种用法是错误的,改为:

intk=a.length;

for(inti=0;i

a[i].f();

}

 不要试图用专门处理去取代条件判定,我们要做的是把数组越界的条件排除掉而不是等它越界了再去处理专门。

九、接口和抽象类

抽象和继承是面向对象的重要特点,使用接口和抽象类能够使得程序的实现细节得到封装,耦合度降低,更容易随着需求的变化而扩展。

下面对一些需要注意的地点进行说明:

1.只从抽象类继承

使用继承时要慎重,继承代表〝一样化/专门化〞的关系,继承会使子类依靠于父类,假如父类的实现发生改变那么子类的实现将不得不发生改变。

因此,在使用继承时一样不要从具体类继承,只有抽象类才用来继承,因为抽象类能够用抽象方法来提供扩展,而抽象类里的具体方法一定要确保是需求比较固定的部分,以后都可不能有任何的改动。

2.不覆写父类的具体方法

在子类中能够对父类的方法进行重载,也确实是父类里实现了一个方法,子类用同样的函数名去重新实现那个方法。

但这种做法是错误的,子类只应该去实现父类的抽象方法,而不是去实现父类已有的具体方法。

假如需要如此做只能说明父类的方法不能作为具体方法而存在,将它改为抽象方法。

3.不在接口中定义属性

这是保全中的一个bug。

为了使保全算功能便于扩展,定义了EdorAppConfirm接口:

publicinterfaceEdorAppConfirm{

publicCErrorsmErrors=newCErrors();

publicbooleansubmitData(VDatacInputData,StringcOperate);

publicVDatagetResult();

}

然后再实现那个接口,比如:

publicclassGrpEdorNIAppConfirmBLimplementsEdorAppConfirm{

publicCErrorsmErrors=newCErrors();

publicbooleansubmitData(VDatacInputData,StringcOperate){

if(!

dealData()){

returnfalse;

}

...

}

}

最后再调用那个接口:

ClasstClass=Class.forName("com..lis.bq.GrpEdor"+

tEdorType+"AppConfirmBL");

EdorAppConfirmtGrpEdorAppConfirm=(EdorAppConfirm)

tClass.newInstance();

VDatatVData=newVData();

tVData.add(iLPGrpEdorItemSchema);

tVData.add(mLJSGetEndorseSchema);

tVData.add(mGlobalInput);

tVData.add(tLPGrpEdorMainSet);

if(!

tGrpEdorAppConfirm.submitData(tVData,

"APPCONFIRM||"+tEdorType)){

CError.buildErr(this,"保全项目"+tEdorType+

"试算时失败!

失败缘故:

"+tGrpEdorAppConfirm.mErrors.

getFirstError());

returnfalse;

}

那个地点本意是假如算费出错returnfalse并返回错误缘故,但实际上不可能得到错误缘故。

因为EdorAppConfirm和GrpEdorNIAppConfirmBL中都定义了mErrors,现在返回的是接口中的mErrors。

假如把GrpEdorNIAppConfirmBL中的mErrors去掉能够解决那个bug,然而在接口中定义public的属性不是好的做法,因此可考虑把mErrors换成getErrors方法。

4.接口和抽象类的组合使用

当我们预备使用抽象时会考虑是使用接口还使用抽象类,因为它们各有优缺点,接口和抽象类最大的区别是抽象类能够有具体实现而接口是纯抽象的。

假如用抽象类那么写在抽象类中的具体实现代码的改动会导致各级子类的改动,假如使用接口幸免了那个问题但又带来了另一个问题,确实是各个实现接口的子类可能有重复的代码,如爱护也需要爱护多处地点。

因此比较好的方法是先定义一个接口,然后定义一个抽象类来实现那个接口,然后各个具体实现类继承那个抽象类,并把重复的实现代码放到抽象类中。

那个地点要注意的是放到抽象类中的代码一定要是固定的,可不能随需求改动的部分。

十、其它需注意地点

1.clone()的使用

java的clone机制使我们能够专门方便的进行使用对象的复制,只需实现Cloneable接口就行了。

如:

publicclassMyObjectimplementsCloneable{

publicObjectclone(){

try{

returnsuper.clone();

}catch(CloneNotSupportedExceptione){

thrownewInternalError();

}

}

}

但要注意的是java分为深clone和浅clone,它们区别是对象内部的成员属性在clone时是否处理为引用。

假如仍旧保留为引用,那么称为浅clone,反之称为深clone。

而java默认是浅clone,假如要实现深clone还需要对各个成员属性分别进行clone操作。

2.Javascript脚本的执行效率

IE执行脚本取object的length效率是专门低的。

不要在循环结构中的操纵变量上直截了当使用〝XX.length〞,应幸免如下程序段的写法:

vartFlag="";

for(i=0;i

if(fm.QueryType[i].checked){

tFlag=fm.QueryType[i].value;

break;

}

}

应改为:

countOfQueryType=fm.QueryType.length;

for(i=0;i

当例子中的

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 人文社科 > 法律资料

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2