数组之间的拷贝使用System.arrayCopy更加高效
byte[] ReceiveBytes = new byte[length1+ length2];
for (int i = 0; i < length1; i++) {
ReceiveBytes[i] =ReceiveBytes_temp1[i];
}
避免创建已经存在的Big Integer对象 ,如:(BigDecimal.ZERO,BigDecimal.ONE, BigDecimal.TEN)
避免创建已经存在的Boolean 对象;如:Boolean.TRUE, Boolean.FALSE
有Final修饰符的成员变量必须是静态的
避免显示调用垃圾回收
低效利用使用keySet迭代器而不是entrySet迭代器。
使用entrySet效率会比keySet高
for (String key : map.keySet()) {
//to do some thing
}
for (Entry entry : map.entrySet()) {
//to do some thing
}
避免在循环中使用“+” 连接字符串。使用Stringbuffer 或Stringbuilder
定义为Private类型方法从未被调用,应该被删除
使用ArrayList 替换Vector
如果从数组转换成一个List,用Arrays. AsList() 替换遍历数组的形式转换
避免创建无用的局部变量,如:
String s = getxx();
return s;
直接替换为return getxx();
当参数是单个字符的时候使用 String.indexOf(char)替换String.indexOf(String)
比如:用s.indexOf(‘a‘) 代替s.indexOf(“a”)
如:String s = new String();
正确写法 String s = “”;
如:String s = new String(“test”);
正确写法 String s = “test”;
Long, Integer, Short, Character, and Byte 使用valueOf代替直接实例化
Number 类型从-128 到127会缓存到常量池,可以节省内存
Integer i = new Integer(4);
Integer j = new Integer(4);
System.out.println(i==j);// false
i = Integer.valueOf(4);
j = Integer.valueOf(4);
System.out.println(i==j); // true
i = Integer.valueOf(128);
j = Integer.valueOf(128);
System.out.println(i==j);//false
对原始值进行装箱然后立即把它强制转换为另外一种原始类型。例如:
new Double(d).intValue()应该直接进行强制转换例如:(int)d
无用的包导入
无用的局部变量
未用的常规参数:避免传递给方法或构造器不使用的参数
代码中包含TODO注释
避免使用空代码块
有时候两个 if 语句可以通过布尔短路操作符分隔条件表达式组合成一条语句
如:
If(a==b){
If(c==1){
//do some thing
}
}
避免在 BigDecimal 类型的构造方法中用小数类型的字面量:人们常常以
为”new BigDecimal(0.1)”能精确等于 0.1, 其实不然,它等于“ 0. 1000000000000000055511151231257827021181583404541015625 ”,这 种状况的原因是 0.1 不能精确的表示双精度类型,因此,传入构造器的 long 类型不等于 0.1 ,而传入 String 类型的构造器 new BigDecimal(“0.1”) 可以精确等于 0.1, 故推荐这种情形时用 String 类型的构造器
破坏空检查:如果自身抛出空指针异常空检查就会遭到破坏,比如你使用 || 代替 && ,反之亦然。
if (string!=null ||!string.equals("")) { // 这里应该是&&
return string;
}
关闭资源:确保这些资源(譬如:Connection,Statement,和 ResultSet 对象)总在使用后被关闭
对象相等性比较:使用 equals()比较对象的引用,避免使用”==”来比较
String的split,replaceAll等方法传递的参数是正则表达式,正则表达式本身用到的字符需要转义,如:句点符号“.”,美元符号“$”,乘方符号“^”,大括号“{}”,方括号“[]”,圆括号“()”,竖线“|”,星号“*”,加号“+”,问号“?”等等,这些需要在前面加上“\\”转义符。
如:s = s.replaceAll(".", "/"); 应该使用s =s.replaceAll("\\.", "/");
明显的无限循环
明显的无限迭代循环,将导致堆栈溢出
重写equals 后必须重写hashCode
避免equals()方法和 null 比较
单元测试必须包含断言,而不是简单的打印结果后看输出。
对于不变类型的无用操作:对于不变类型对象 (String,BigDecimal 或BigInteger) 的操作不会改变对象本身,但操作结果是产生新的对象,所以操作的结果是错的
如:BigDecimal a=new BigDecimal(10);
a.add(newBigDecimal(5));
正确的写法:
BigDecimal bd=new BigDecimal(10);
bd = bd.add(new BigDecimal(5));
避免用== or != 比较 String
StringBuffer sb = new StringBuffer('c');
字符 c 会转换为 int 值,作为 StringBuffer 的初始化大小参数
public void doGet(HttpServletRequestrequest,HttpServletResponse response)throws ServletException,IOException{
String v = request.getParameter("v");
PrintWriter out = response.getWriter();
out.print("协议版本号不对,v="+v);
out.close();
}
这里字符串v没有作过滤,直接返回给用户,有可能操作XSS攻击
在代码中在JSP输出中直接写入一个HTTP参数,这会造成一个跨站点的脚本漏洞
private static SimpleDateFormat dateFormat = newSimpleDateFormat("yyyy-MM-dd");
避免使用静态的DateFormat,DateFormat 是非线程安全的
同上
清空集合使用clear() 代替removeAll()
在代码中避免使用e.printStackTrace,使用logger代替
代码中禁止使用System.println
While for 循环If Else 代码块必须使用大括号
为局部变量赋值,但在其后的没有对她做任何使用。通常,这表明一个错误,因为值从未使用过。
此方法使用相同的代码,以实现两个有条件的分支。检查以确保这是不是一个编码错误
Checkstyle常见错误和警告提示见下表所示:
错误提示 |
错误说明 |
missing a javadoc comment |
缺少类注释 |
Line longer than X characters |
行长度超过X个字符(包括空格) |
Return count is X(max allowed 3) |
一个方法内的返回数量是X(最大值只能为3) |
Nested if-else depth is X(max allowed is 3) |
最大的if-else嵌套层数为X(最大只能为3) |
Array brackets at illegal position |
数组的方括号“[]”的位置不正确(检查数组类型的定义是String[] args,而不是String args[]) |
Line matchs the illegal pattern 'System\.out\.println' |
本行包含System.out.println语句 |
ctor def modifier at indentation level 8 not at corrent indentation 4 |
缩进不正确,一般是因为没有在Eclipse中使用4个空格代替tab键引起。 |
'static' modifier out of order with the JLS suggestions |
static修饰符没有按照JLS的建议来排序(eg.写成public final static...应该改成public static final) |
Name 'X' must match pattern '^[A-Z][A-Z0-9][_A-Z0-9+]$'(正则表达式) |
名称不符合正则表达式'^[A-Z][A-Z0-9][_A-Z0-9+]$'(即为大写字母,数字、下划线等)。 一般在静态变量没有大写时提示,包名不是全部消息时提示,类名不是大写开头时提示,方法名不是小写开头时提示 |
Variable access definition in wrong order |
变量定义顺序不正确(例如在类成员变量定义时,将private类型的变量定义在public类型的变量之前) |
Static variable definition in wrong order |
静态变量定义顺序不正确(例如在构造函数之后定义静态变量) |
Instance variable definition in wrong order |
成员变量定义顺序不正确(例如在构造函数之后定义成员变量) |
X is a magic number |
X是一个魔术数字(非0、1、2的数字) |
if construct must use '{}' |
if结构必须使用'{}' |
Got an exception - Unexpected character 0xfffd in identifier |
因为没有设置checkstyle配置文件的charset为UTF-8,而类文件使用UTF-8编码,并且含有中文 |
“{” should be on the previous line |
“{” 应该位于前一行 |
Methods is missing a javadoc comment |
方法前面缺少javadoc注释 |
Expected @throws tag for “Exception” |
在注释中希望有@throws的说明 |
“.” Is preceeded with whitespace |
“.” 前面不能有空格 |
“.” Is followed by whitespace |
“.” 后面不能有空格 |
“=” is not preceeded with whitespace“=” |
前面缺少空格 |
“=” is not followed with whitespace |
“=” 后面缺少空格 |
“}” should be on the same line |
“}” 应该与下条语句位于同一行 |
Unused @param tag for “unused” |
没有参数“unused”,不需注释 |
Variable “X” missing javadoc |
变量“CA”缺少javadoc注释 |
Line contains a tab character |
行含有”tab” 字符 |
Redundant “Public” modifier |
冗余的“public” modifier |
final modifier out of order with the JSL suggestion |
final修饰符的顺序错误 |
Avoid using the “.*” form of import |
Import格式避免使用“.*” |
Redundant import from the same package |
从同一个包中Import内容 |
Unused import-X Import |
import的X类没有被使用 |
Duplicate import to line X |
重复Import同一个内容 |
Import from illegal package |
从非法包中 Import内容 |
“while” construct must use “{}” |
“while” 语句缺少“{}” |
Variable “X” must be private and have accessor method |
变量“X”应该是private的,并且有调用它的方法 |
Variable “X” must match pattern “^[a-z][a-zA-Z0-9]*$” |
变量“X”不符合命名规则“^[a-z][a-zA-Z0-9]*$” |
“(” is followed by whitespace |
“(” 后面不能有空格 |
“)” is proceeded by whitespace |
“)” 前面不能有空格 |
PMD
检查java源文件中的潜在问题。
主要包括:
- 空try/catch/finally/switch语句块
- 未使用的局部变量、参数和private方法
- 空if/while语句
- 过于复杂的表达式,如不必要的if语句等
- 复杂类
CheckStyle
检查java源文件是否与代码规范相符
主要包括
- Javadoc注释
- 命名规范
- Headers
- Imports
- Size冲突和度量,如过长的方法
- Whitespace
- Modifiers
- Blocks
- Coding Problems
- Class Design
- 重复代码
- Miscellaneous Checks
- Optional Checks
配套的Bug解释模式
为了有针对性的使用这个工具,减少bug的误报,提高使用效率,我们选择了10个左右的bug模式,下面就是对这10个模式的解释。
这些bug可能会引起程序的性能或逻辑问题.
需要说明的是,findbugs能检测的bug pattern远不仅于此,甚至可以定制自己的探测器,因此,这个文档会不断扩充,同时,也欢迎大家不断探索和分享使用实践.
大的分类主要包括以下几种:
Bad practice |
不好的习惯 |
Correctness |
代码的正确性 |
Dodgy |
小问题 |
Malicious code vulnerability |
恶意代码 |
Internationalization |
国际化问题 |
Performance |
性能问题 |
Security |
安全性问题 |
Multithreaded currectness |
线程问题 |
Experrimental |
实验性问题 |
FindBugs常见错误描述和解决方法
(一)[DLS_DEAD_LOCAL_STORE]
描述: Dead store to 未使用的局部变量
解决方法:局部变量定义后未使用;实例化对象后又重新对该对象赋值
(二) [ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD]
描述:Write to static field 通过实例方法更新静态属性
常见于常量类,直接通过类名.常量名获取的方式违背了封装的原则,findbugs不提倡使用,而如果将常量改成静态成员变量,又因为spring不支持静态注入导致不能实现,解决方法是非静态的setter调用静态的setter方法给静态成员变量赋值。
解决方法:
常量类F:
class F{
public static String a = “123”;
}
常量a改为静态成员变量,通过F.getA()获取,且由于spring不支持静态注入,改为:
class F{
private static String a;
public static Integer getA() {
return a;
}
public void setA(String a) {
setAValue(a);
}
public static void setAValue(String a) {
F.a = a;
}
}
(三) [BX_UNBOXING_IMMEDIATELY_REBOXED]
描述: Boxed value is unboxed and then immediately reboxed 装箱的值被拆箱,然后立刻重新装箱了
常见的是三目运算时,同时存在基本类型和包装类型。
解决方法:
Integer a = null;
//...
a = (a == null)?0:a;
此问题在于a不为null时,会被拆箱,赋值时再装箱。这是自动装箱拆箱的特性,只要运算中有不同类型,当涉及到类型转换时,编译器就会向下转型,再进行运算。修改方法,统一类型:
Integer a = null;
//...
a = (a == null)?Integer.valueOf(0):a;
(四) [SE_BAD_FIELD]
描述: Non-transient non-serializable instance field in serializable class在可序列化的类中存在不能序列化或者不能暂存的数据
解决方法:
方法1:序列化该对象
方法2:当采用struts2框架开发,不可避免的此问题会大量出现,因为ActionSupport实现了序列化接口,action继承了此类,而service没序列化,所以在action中引用service对象时提示此错误,最简单的解决方法是将service对象声明成transient,即service不需要序列化
方法3(未验证):To avoid java serialization you need to implement writeObject() and readObject() method in your Class and need to throw NotSerializableException from those method.(action中实现这两个方法?)
private void writeObject(java.io.ObjectOutputStream stream) throws java.io.IOException {
throw new java.io.NotSerializableException( getClass().getName() );
}
private void readObject(java.io.ObjectInputStream stream) throws java.io.IOException, ClassNotFoundException {
throw new java.io.NotSerializableException( getClass().getName() );
}
(五) [NP_LOAD_OF_KNOWN_NULL_VALUE]
描述: Load of known null value加载已知是null的值
解决方法:已知方法参数为null是,直接传递null而不是参数名
(六) [REC_CATCH_EXCEPTION]
描述: Exception is caught when Exception is not thrown 过泛地捕获异常或捕获异常后未做任何处理
解决方法:异常分类捕获(至少要打印出此异常对象)
(七) [NP_NULL_PARAM_DEREF]
描述: Null passed for nonnull parameter 把空值传给了非空的参数
解决方法:增加非空判断
(八) [NP_IMMEDIATE_DEREFERENCE_OF_READLINE]
描述: Immediate dereference of the result of readLine() 立即引用了readLine()的结果
解决方法:判断readLine的结果是否为空
(九) [EI_EXPOSE_REP] 恶意代码漏洞
描述:may expose internal representation by returning getter方法返回引用类型
eclipse自动生成的引用类型(Object、数组、Date等)的getter、setter方法会得到或通过对可变对象的引用操作而暴露代码内部实现,解决方法很多,只要返回的或赋值的对象不是原引用对象即可。
解决方法:
以Date类型为例:
public Date getHappenTime() {
if(happenTime != null){
return (Date) happenTime.clone();
}
return null;
}
(十) [ EI_EXPOSE_REP2] 恶意代码漏洞
描述:may expose internal representation by storing an externally mutable object into setter方法返回引用类型
eclipse自动生成的引用类型(Object、数组、Date等)的getter、setter方法会得到或通过对可变对象的引用操作而暴露代码内部实现,解决方法很多,只要返回的或赋值的对象不是原引用对象即可。
解决方法:
以Date类型为例:
public void setHappenTime(Date happenTime) {
if(happenTime != null){
this.happenTime = (Date) happenTime.clone();
}else{
this.happenTime = null;
}
}