序言:
软件构造实验中要求使用idea的第三方插件进行debug ,debug常用插件有两种:findbug 和spotbug,其中spotbug的功能更为全面,下面对笔者在编程中遇到的各种小错误进行总结如下:(不算全面,但是都是写实验中所有得错误)
一.规范性错误:
Bad practice
1.Confusing method name:
此错误提示表示 为程序中的类名,域,和方法进行命名时法生规范性错误,往往为大小写的规范性错误;
(1)类名的首字母应该大写
(2)类中的方法的首字母因该小写并且名字最好用动词来表示,比如 toString(), getDistance(),getName()等简单直观的动词来表示方法的用途。
(3)域名的首字母(Field)应该小写
2. equals() method does not check for null argument
笔者一开始学习java时总是犯这个错误,在重写equals 方法时直接进行比较而没有考虑空指针的情况,空指针的代码应该先进行是否为空指针的判断,否则一旦传入空指针程序会直接崩溃而不是返回false。具体案例 会在下面的错误一块说明。
3.Equals method for Airplane assumes the argument is of type 某某某
此种情况和上述情况一样发生在equals中的不良习惯: 在进行比较时直接将Object that 强制转换为进行比较的对象,这么做是错误的,因为不能直接将Object 假定为 要比较的类及其子类型,如下为错误案例:
@Override
public boolean equals(Object that){
Airplane thatPlane = (Airplane) that;
if(this.Name.equals(thatPlane.getName())){
return true;
}
else return false;
}
上图存在两个bug,一个是上文中所说的没有进行空指针的判断,第二个是没有进行类型的判断,即不能直接假定这个转换可以执行,而是需要进行 判断传入的对象是不是进行比较的对象或者是不是进行比较的对象的子类型。
所以修改过后的代码如下
@Override
public boolean equals(Object another) {
if (another == null)
return false;
if (!(another instanceof Plane))
return false;
Plane ap = (Plane) another;
if (ap.getPlaneNo().equals(this.getPlaneNo()))
return true;
return false;
}
(先进行判断空指针,后用instanceof 进行类型的判断。)
4.类名 + may fail to close stream
这种情况一般是所提示的类或方法中创建一个IO流对象,(比如FileReader, BufferedReader 等)。这可能会导致文件描述符泄漏。通常最好使用finally块来确保流已关闭。
虽然大多数情况下并不影响程序的运行,但是spotbug会将这类错误挑出,所以建议在Reader等IO流对象读入完成时加入close 语句。
FileReader reader = new FileReader("路径");
BufferedReader br = new BufferedReader(reader);
// 读入完成时 reader.close(); br.close();
二.错误代码(DodyCode)
1.Switch statement found where one case falls through to the next case
此种情况就是 switch的选择语句中有的语句忘记写break ,导致直接进入下一个case中,连续执行了两次。
2.Switch statement found where default case is missing
switch语句要考虑各种情况的发生,如果case的几种情况都没有发生时需要进入default中进行处理。
错误代码
switch(op){
case 1: System.out.println("不能改成这个状态");
case 2: plans.get(i).Allocate();break;
case 3: plans.get(i).Run();break;
case 4: plans.get(i).Finish();break;
case 5: plans.get(i).Cancel();break;
case 6: plans.get(i).Block(); break;
}
在写状态转移函数时没有case 1忘记写break。除此之外,switch语句中也没有default情况的处理。
3.Questionable for loops
一般是 两层for循环中的设计问题或者笔误(实际编程中多为笔误情况)
比如
Complicated, subtle or wrong increment in for-loop
for (int i = 0; i < entries.size() - 1; i++)
for (int j = i + 1; j < entries.size(); i++)
这种错误。
4.Immediate dereference of the result of readLine()
一般情况java中读入文件可以采用两种方式:
Scanner 和BufferedReader来读入语句。
但是在使用reader需来获取输入的时候首先要判断其是否为空,原因如下:
BufferedReader在读取一行文字的时候通常要结合.trim()方法使用,表示 去除读取行的头尾空格。
但是如果直接按照如下方法使用会报错:
String Name;
Name = reader.readLine().trim();
这是因为如果 Name 的输入为空的话,调用trim()方法去掉头尾空格 会直接因为 空指针而导致程序崩溃。
所以解决方法是,先读入,判断这个输入是否为空,再去掉头尾空格。
String Name = reader.readLine();
if(Name== null){
System.out.println("读入不能为空");
return false;
}
Name = Name.trim();
5.Dead store to local variable或
Dead store to local variable that shadows field
这个错误一般对应着程序中存在无用的代码,程序运行中这串代码没有使用过,往往在调试程序中容易产生这种情况而忘记删除。 直接删除改代码即可,对程序没有影响。
Unread field
这个错误提示和上述同理,不影响程序运行但是多出一些无关紧要的代码,直接删除即可。
三.潜在的性能问题(Performance)
1.Method concatenates strings using + in a loop
在重写toString()方法时,经常会需要将两个或多个字符串组合起来,这时候如果用 “+”的方式直接相加会导致程序的性能降低,因此spotbug提示该处应该使用StringBuilder 结合append(),substring()的方法进行字符串的组合
实例
String s = "";
for (int i = 0; i < field.length; ++i) {
s = s + field[i];
}
上面的写法应该改成:
StringBuffer buf = new StringBuffer();
for (int i = 0; i < field.length; ++i) {
buf.append(field[i]);
}
String s = buf.toString();
2.Unread field
这个错误提示和上文中提到的错误 同理,程序在调试过程中可能存在一些无用代码,不影响程序运行,但是多出一些无关紧要的代码,直接删除即可。
3.Boxing/unboxing to parse a primitive
错误提示: 从字符串中创建一个已装箱的原语,只是为了提取未装箱的原语值。只调用静态parseXXX方法更有效。
这种情况一般是 ValueOf的使用不恰当,原因如下:
Integer.parseInt(s)的作用就是把字符串s解析成有符号的int基本类型
Integer.valueOf(s)把字符串s解析成Integer对象类型,返回的integer 可以调用对象中的方法。
String x = "1";
test.setX(Integer.valueOf(x)); // setX()需要的参数为int
代码中使用的Integer.valueOf()经历了String - int - Integer - int的过程,使用Integer.paseInt()可以节约int 转Integer的过程
s所以应该修改为:
String x = "1";
test.setX(Integer.parse(x));
四.潜在的跨平台错误:
1.Found reliance on default encoding
在使用读写方法:FileReader, BufferedReader时,FileWriter FileReader 等等 是不带编码格式的,如果不指定好编码的话就默认为系统默认编码,但是一旦使用就变成了一个技术债务,因为系统的默认编码是不可预知的
如果要避免这个错误,需要将编码指定好
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
比如BufferedReader,可以直接为其设定编码
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in,"UTF-8"));~
如果是FileReader ,可以直接设定编码或 在BufferedReader中进行定义:
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("路径"),"UTF-8"));
以上为笔者在软件构造写实验中spotbug环节遇到的所有错误,得出总结:spotbug真是个好东西~~,意识到自己代码得不规范和 粗心之处!