声明审查
• 避免命名覆盖
<script language="JavaScript1.1" src="http://ad.cn.doubleclick.net/adj/messagingplus.zdnet.com.cn/developer/code;sz=1x1;ord=995643307?" type="text/javascript"> </script> <script language="VBScript" type="text/javascript"> on error resume next ShockMode = (IsObject(CreateObject("ShockwaveFlash.ShockwaveFlash.5"))) </script> |
命名隐藏的审查有以下几种情况:
• 类成员变量被局部变量隐藏:因类方法体中的局部变量和类成员变量具有相同的名字,而使成员变量被屏蔽。一般情况下,类构造函数或赋值方法的入参和类中的成员变量保持相同的名字,类的成员变量通过 this 显式标识,这种情况下该审查规则不会警示。该审查内容对应于“ Declaration Style ”下的“ Hiding Names ”设置项。
代码清单 7 局部变量隐藏成员变量
• public class HideName
• {
• int index;
• void func()
• {
• int index;// 隐藏了成员变量 index ,应改成另一个名字,如 int newIndex;
• // do something
• }
• void setIndex(int index)
• {
• this.index = index;// 该语句行中带 this 显式引用成员变量进行赋值,审查规则将不报警
• index++;// 该语句行没有 this 显式引用,审查规则将报警
• }
• }
• 子类成员变量隐藏父类成员变量:子类成员变量和可继承的父类成员变量名字相同。该审查内容对应于“ Declaration Style ”下的“ Hiding Inherited Field ”设置项。
代码清单 8 子类成员变量隐藏父类成员变量
• class Window
• {
• protected int style;
• }
•
• class Button extends Window
• {
• protected int style;// 具有和父类相同的成员变量,应改为另一个名字,如 anStyle
• }
• 子类覆盖父类静态方法:和非静态的方法覆盖不一样,静态的父类方法不应被子类覆盖,该审查内容对应于“ Declaration Style ”下的“ Hiding Inherited Static Methods ”。
代码清单 9 子类覆盖父类静态方法
• class Animal
• {
• static void oper1(){}
• static void oper2(){}
• }
•
• class Elephant extends Animal
• {
• static void oper1() {}// 隐藏了父类中的静态方法,应取另一个名字,如 anOper1()
• static void oper2() {}// 隐藏了父类中的静态方法,应取另一个名字,如 anOper2()
• }
成员变量和局部变量的隐藏,常常会使开发人员张冠李戴,犯一些不经意的错误,而子类隐藏父类的成员和静态变量常常是由于没有注意到父类中已经具有相同的名字而引起的,由此而生产的程序 Bug 由于其隐身性强,是很难被发现,该审查项帮助你规避这一问题。
• 使用适合的修饰符
<script language="JavaScript1.1" src="http://ad.cn.doubleclick.net/adj/messagingplus.zdnet.com.cn/developer/code;sz=1x1;ord=1298129404?" type="text/javascript"> </script> <script language="VBScript" type="text/javascript"> on error resume next ShockMode = (IsObject(CreateObject("ShockwaveFlash.ShockwaveFlash.5"))) </script> |
static,final, 可视域等修饰符可以用于修饰,类的成员变量、方法及内部类,使用恰当的修饰符,类的封装性、承继性将得到增强,同时还防止了错误的调用。修饰符的审查主要包括以下 3 点:
• 常量应该被标识为 final :这样可以阻止程序对其值进行更改,也可以防止子类的覆盖。该审查内容对应“ Declaration Style ”下的“ Constant Variables Should Be Final ”设置项。
• 不被外部调用的成员应该声明为 private :该审查项主查为了强化类的封装性和内聚性,使类对应的接口更加清晰。该审查项并不对 public 的成员生效,被标识为 public 的成员将被看成是希望向外开放的。该审查内容对应“ Declaration Style ”下的“ Member Can be made Private ”设置项。在接到该审查项的报警时,开发人员需要根据实际情况作出判断,以将确实可以设置为 private 的成员改正过来。
• 可设置为 static 的成员: JBuilde r2005 分析哪些成员变量及成员内部类可以置为 static :即那些在静态环境下使用的成员变量和没有引用容器类非静态成员和方法的内部类。该审查内容对应“ Declaration Style ”下的“ Member Can be made Static ”设置项。
• 有关子类覆盖的审查
JBuilder 将对继承覆盖中的以下几个方面进行审查:
• equals() 和 hashcode() 方法必须同时覆盖:类覆盖了 Object 类中所定义的 equals(Object obj) ,同时也必须覆盖 Object 类中所定义的 hashCode() 方法,因为必须保证用 equals(Object obj) 判断为相同的对象拥有相同的 hashCode 值。该审查内容对应“ Declaration Style ”下的“ Override Hash code when you override Equals ”设置项。
• 将父类中的非抽象类覆盖成抽象类:没有理由将父类的非抽象类覆盖成子类的抽象类,甚至可以断定这是编码上的偏误。该审查内容对应“ Declaration Style ”下的“ Overriding a Non-Abstract Method with an Abstract Method ”设置项。
• 在子类中声明和父类相同的 private 方法: private 方法是不能被覆盖的,一般情况下,可以被子类覆盖的方法是 protected 或 public 。该审查内容对应“ Declaration Style ”下的“ Overriding a Private Method ”设置项。
• 代码排列及先后顺序
对类文件的代码进行良好的组织,可以增强代码的可维护性和可读性, JBuilder 2005 对代码的排列及先后顺序进行以下方面的审查:
• 类成员的排列:根据 Sun 的代码编程惯例,成员的先后顺序以可视域从大到小排列,即: public->protected-> 默认 ->private 。类中的成员按如下方式排列:
1) 静态成员变量排在最前面,静态成员间以可视域从大到小排列。
2) 第二位是实例成员变量,实例成员变量间以可视域从大到小排列。
3) 第三位是构造函数。
4) 第四位是成员方法
该审查内容对应“ Declaration Style ”下的“ Order of Declaration of Class Members ”设置项。
• 将重载方法列在一起:类的重载方法完成相似的功能,具有相同的方法名,将它们列在一起可以增强程序的可读性,且不容易遗漏掉某个功能。该审查内容对应“ Declaration Style ”下的“ Place Methods with Same Name Together ”设置项。
• 将 public 类放在前面:对于包含多个类的程序文件,应该把 public 的类放在前面。该审查内容对应“ Declaration Style ”下的“ Place Public Class First ”设置项。
• 命名风格
• 取消不良命名习惯
良好的命名风格在遵守 Java 命名语法之上,对命名提出了更高的要求,良好的命名风格必须遵守以下的命名规则:
• 类或接口必须以大写字母打头。
• 方法、属性、成员变量、局部变量以小写字母打头,且不以“ _ ”或“ $ ”打头。
• 常量的所有字母都大写。
• 异常类以 Exception 结尾。
良好命名对应“ Naming Style ”下的“ Naming Conventions ”设置项。
• 建立和国际接轨的包名
包名应该用顶级域名打头,如 com , org , edu 等,或者用国家代码如 cn , ru 等。
一般公司和组织都对包名前两级都有严格的规则,如 IBM 公司的类以 com.ibm 打头, apache 以 org.apache 打头,第三级才是具体的项目或产品名称,这样的包命名就象三维网的 URL 命名一样已经成为了一种国际通用的准则。对此没有作出严格规定的公司,开发负责人应该推动建立起符合这一命名规则的规范。
该审查项,默认情况下没有激活,可以通过“ Naming Style ”下的“ Package Name ”的设置项激活。
• 避免用过于简单的变量名
<script language="JavaScript1.1" src="http://ad.cn.doubleclick.net/adj/messagingplus.zdnet.com.cn/developer/code;sz=1x1;ord=127422765?" type="text/javascript"> </script> <script language="VBScript" type="text/javascript"> on error resume next ShockMode = (IsObject(CreateObject("ShockwaveFlash.ShockwaveFlash.5"))) </script> |
除了循环体中的临时变量,及一些没有特殊意义的常见数据类型,应该尽量避免使用一个字符作为变量。那些无特殊意义且常见的数据类型,所选取的单字符变量名必须按表 1 进行命名:
表 1 变量本身无意义的常见数据类型允许的单字符变量
常见数据类型 | 单字符变量名 | 常见数据类型 | 单字符变量名 |
byte | b | double | d |
char | c | Object | o |
int | i , j , k | String | s |
long | l | Exception | e |
float | f |
|
|
此外,为了减少潜在的冲突,避免不必要的混淆,不允许以大写域名或国家代码作变量名。
代码清单 10 取有意义的变量名
• void method(double d)
• {
• int i;
• Exception e;
• char s;// 应该改为 c
• Object f;// 应该改为 o
• String k;// 应该改为 s
• Object UK;// 和英国国家代码相同,应改为其他的名字,如 ukObj
• Object COM;// 和域名相同,应改为其他的名字,如 obj_1
• }
该审查项,在默认情况下没有激活,可以通过“ Naming Style ”下的“ Use Conventional Variable Names ”的设置项激活。
• 潜在错误审查
• 聚焦 switch
由于 switch 流程控制语句语法的特殊性,编写程序时需要特别注意,否则将会埋下祸根, JBuilder 从以下 3 个方面对 switch 进行审查:
• 有无对前面没有 break 语句的 case 从句作标识:根据 Sun 编码惯例,程序入口点从一个 case 进入,直接到达下一个 case 代码段,即前一个 case 没有对应的 break 语句时,在跨过的地方必须给出一个显示的注释,表示是特定流程控制的要求,而非无意遗漏。来看下面的代码:
代码清单 11 没有 break 的 case 从句
• switch (c) {
• case '+':
• ...
• break;
• case '-':
• ...
• case 'n':
• ...
• case ' ': case '/t':
• ...
• break;
• }
假设在代码清单 11 的第 7 行之前,是因为疏忽而遗漏了一个 break 语句,第 9 行之前是逻辑需要而故意不加 break 语句,则将代码更改为:
代码清单 12 更正的 switch 代码
• switch (c) {
• case '+':
• ...
• break;
• case '-':
• ...
• break;
• case 'n':
• ...
• // 继续运行到下面
• case ' ': case '/t':
• ...
• break;
• }
该审查内容对应于“ Possible Errors ”下的“ Break Statement is Missing before Case clause ”设置项。
• 在 switch 中出现非 case 的标签:在 Java 语句中有两个标签,即 case 分支标签,另一个则是语句标签,如果 case 分支标签语句误删或遗漏了 case 关键字,则 case 分支标签将变成语句标签,而编译器无法识别这个错误。
代码清单 13 case 分支中缺少 case 而使标签发生质变
• public class CaseLabel
• {
• /** 点 */
• public static final int POINT = 1;
• /** 线 */
• public static final int LINE = 2;
• /** 多边形 */
• public static final int POLYGON = 3;
•
• public String getFigureType (int kind)
• {
• String tempName = null;
• switch (kind)
• {
• case POINT:
• LINE:// 该语句缺少 case, 编译器将其作为语句标签处理,并不会发生语法错误
• // 但该方法传入常量 LINE 时,将转到 default 分支中,而非到达这晨,故
• // 应该将该行语句更改为 case LINE:
• tempName = "POINT and LINE";
• break;
• case POLYGON:
• tempName = "POLYGON";
• break;
• default:
• tempName = "UNDEFINE";
• }
• return tempName;
• }
• }
该审查内容对应于“ Possible Errors ”下的“ Non-Case Label in Switch statement ”设置项。
• 有错误嫌疑的 break 和 continue : break 和 continue 用于 switch 和循环中的跳转控制, break 用于提前结束循环以及从 switch 中退出, break 的这种“多态性”使得在循环体中内嵌 switch 语句时,常会带来一些隐患。即开发者本希望退出外层循环,结果却只退出内层的 switch 语句而已。 JBuilder 2005 对这项内容的审查包括以下方面:
• switch 内嵌于循环体中,且 case 从句中包含了不位于分支块最后位置的 break 语句。
• switch 内嵌于循环体中,且 case 从句既使用了 break ,又使用了 continue ,但两者的效果却是一样的。
• break 或 continue 语句中使用了不必要的语句标签。
请看下面的代码:
代码清单 14 有错误嫌疑的 break 和 continue
• void scan(char[] arr)
• {
• loop:
• for (int i = 0; i < arr.length; i++)
• {
• switch (arr[i])
• {
• case '0':case '1':case '2':case '3':case '4':
• //5~6 的数字
• case '5':case '6':case '7':case '8':case '9':
• {
• if (processDigit(arr[i]))
• {
• continue loop; //loop 语句标签没有必要
• }
• else
• {
• break; // 该 break 不会结束 for 循环 , 应该使用 break loop 才可结束循环
• }
• }
• case ' ':case '/t':
• {
• processWhitespace(arr[i]);
• continue; // 应该使用 break 而非 continue
• }
• default:
• processLetter(arr[i]);
• break;
• }
• }
该审查内容对应于“ Possible Errors ”下的“ Suspicious Break/Continue ”设置项。
• 避免对浮点值进行等值逻辑判断
<script language="JavaScript1.1" src="http://ad.cn.doubleclick.net/adj/messagingplus.zdnet.com.cn/developer/code;sz=1x1;ord=80819781?" type="text/javascript"> </script> <script language="VBScript" type="text/javascript"> on error resume next ShockMode = (IsObject(CreateObject("ShockwaveFlash.ShockwaveFlash.5"))) </script> |
浮点数都是一定精度的数据,由于内部表示的误差,往往字面上相同的两个浮点数,其内部表示也不完全相同。故此应避免对浮点值数进行等值逻辑判断,而应采用逻辑比较判断。
代码清单 15 语句中包含浮点等值判断
• void calc(double limit)
• {
• if (limit == 0.0)// 应改为通过和较小值比较来判断,如 if(Math.abs(limit) < 0.0000001)
• {
• System.out.println(" the float-point number is exactly 0");
• }
• }
该审查内容默认未激活可以通过“ Possible Errors ”下的“ Suspicious Break/Continue ”设置项来激活审查。
• 添加 () 清晰化复杂的表达式
写复杂的表达式时不应过度依赖运算操作符的计算优先顺序,而应养成使用“ () ”的好习惯,当一个逻辑表达式由多个逻辑运算组成时,应该用“ () ”划分不同的部分。
代码清单 16 用括号清晰化表达式
• boolean a, b, c;
• ...
• if (a || b && c) // 应该替换成 if ((a || b) && c)
• {
• ...
• }
该审查内容默认未激活可以通过“ Possible Errors ”下的“ Mixing Logical Operators Without Parentheses ”设置项来激活审查。
• 字符串比较
Java 初学者一个常犯的错误是使用“ == ”或“ != ”对字符串进行等值逻辑判断。使用“ == ”将判断两者否是指向相同的对象引用,而非判断两者是否具有值,应该使用 equals() 替代。
代码清单 17 用 equals() 替换“ == ”
• public boolean equals(String s1, String s2)
• {
• return s1 == s2; // 应改为 return s1.equals(s2);
• }
该审查内容对应于“ Possible Errors ”下的“ Use 'equals' Instead of '= =' ”设置项。
(原文地址:http://www.zdnet.com.cn/developer/code/story/0,3800066897,39325960-2,00.htm)