缺陷静态检查 2897
使用工具QAPlug(包含Checkstyle FindBgus PMD)
静态代码检查
缺陷修改意见主要参考:stackoverflow
前言
使用QAPlug工具进行分析,将缺陷主要分为效能、可移植性、可靠性、可维护性、可用性五个方面。本文将分两篇来简单分析代码中缺陷的成因以及解决方案。第一篇将从效能、可移植性、可靠性三方面入手,对每一个错误类型都会给出一个典型的修改案例或意见。
效能 22
Hide Utility Class Constructor 3
工具类应该隐藏public构造器
如果该类仅是实用程序类,那么应该将该类定型并定义一个私有构造函数
这样可以防止默认的无参数构造函数在代码的其他地方使用。此外,还可以将类定型,这样它就不能在子类中进行扩展,这是实用程序类的最佳实践。由于仅声明了一个私有构造函数,因此其他类将无法对其进行扩展,但是将类标记为final仍然是一种最佳实践。
举例
/*
将该类标记为final,以使Sonar(代码检查工具)实际考虑已解决的违规情况。仅添加私有构造函数并不能清除冲突。
*/
public class ServiceLoader {
// 省略...
private ServiceLoader(){
// 不调用
}
// 省略...
}
Unnecessary Local Before Return 19
返回之前不用的本地变量
举例:
// 修改前
public static final int writeJSONString(OutputStream os, //
Charset charset, //
Object object, //
SerializeConfig config, //
SerializeFilter[] filters, //
String dateFormat, //
int defaultFeatures, //
SerializerFeature... features) throws IOException {
SerializeWriter writer = new SerializeWriter(null, defaultFeatures, features);
try {
// 省略...
// 修改前
int len = writer.writeToEx(os, charset);
return len;
// 修改为 return writer.writeToex(os, charset)
} finally {
writer.close();
}
}
可移植性 1
Replace Hashtable With Map 1
用Map替换Hashtable
public Map<Object, Object> createMap(Type type, int featrues) {
if (type == Properties.class) {
return new Properties();
}
if (type == Hashtable.class) {
return new Hashtable();
// 替换为 return new Map();
}
if (type == IdentityHashMap.class) {
return new IdentityHashMap();
}
if (type == SortedMap.class || type == TreeMap.class) {
return new TreeMap();
}
// 省略...
}
可靠性 1597
Avoid Catching Throwable 48
避免捕获Throwable
举例:
在catch里抛出Throwable里不被推荐,因为它的范围极其广泛,这个包含了一些运行时异常,如OutOfMemoryError内容溢出,而这些错误应该被单独管理。
因此具体问题具体分析,请指明具体异常
Clone Throws Clone Not Supported Exception 3
clone方法应该抛出CloneNotSupportedException
举例:
// 原
public Object clone() {
return new JSONArray(new ArrayList<Object>(list));
}
// 修改后
public Object clone() throws CloneNotSupportedException {
return new JSONArray(new ArrayList<Object>(list));
}
Close Resource 11
关闭资源
应该确保资源在使用后关闭 比如InputStream
举例:
public static <T> T parseObject(byte[] bytes, int offset, int len,
Charset charset,
Type clazz,
ParserConfig config,
ParseProcess processor,
int featureValues,
Feature... features) {
// ...
if (charset == IOUtils.UTF8) {
// ...
if (chars_len < 0) {
InputStreamReader gzipReader = null;
try {
gzipReader = new InputStreamReader(
new GZIPInputStream(
new ByteArrayInputStream(bytes, offset, len)), "UTF-8");
// ...
} catch (Exception ex) {
return null;
} finally {
// ...
// 新增gzipReader.close() //此处应该记得抛出IO异常
}
}
// ...
}
Compare Objects With Equals 30
应该用equals方法来比较对象
对于对象的引用应该使用equals方法,原因懂的都懂,不过多赘述。
举例:
static class FloorSegment implements Segment {
public final static FloorSegment instance = new FloorSegment();
public Object eval(JSONPath path, Object rootObject, Object currentObject) {
if (currentObject instanceof JSONArray) {
JSONArray array = ((JSONArray) ((JSONArray) currentObject).clone());
for (int i = 0; i < array.size(); i++) {
Object item = array.get(i);
Object newItem = floor(item);
if (newItem != item) {
//此处判断应修改为 !newItem.equals(item)
array.set(i , newItem);
}
}
return array;
}
return floor(currentObject);
}
Constructor Calls Overridable Method 16
在构造方法中调用覆写的方法不妥当,存在风险
有风险。构造方法调用不是原子的,因此,如果在另一个线程中调用实例方法,那么从构造方法推迟实例方法的调用也同样安全,因为不能保证在(最终)调用线程时完全构造对象。
现在,假设,如果子类对象被完全构造(重点是假设),那么未来的回调将是安全的。换句话说,它不会绕过部分构造的对象,这与其说是危险的,还不如说是访问它。
这是有风险的,因为在完全构建测试对象之前,会将它暴露给第二个线程。
如果需要通过调用hello来控制测试实例的初始化,请考虑使用工厂方法进行实例化。将其与私有构造方法相结合,可以保证在所有测试对象上安全地调用hello:
Design For Extension 270
如果类不是为扩展而设计的,考虑将类final或使方法’write’ static/final/abstract/empty,或为方法添加允许的
Magic Number 1194
魔数:代码中出现的没有说明的数字
具体问题具体分析,FastJson中的魔数主要表现在1024、0xff以及设计特殊需要 没啥好说的
Security Array is stored directly 24
一般是数组浅拷贝和深拷贝的问题
举例:
public Context(FieldInfo[] getters, //
SerializeBeanInfo beanInfo, //
String className, //
boolean writeDirect, //
boolean nonContext){
this.getters = getters;
// 改为 this.getters = Arrays.copyOf(getters, getters.length)
this.className = className;
this.beanInfo = beanInfo;
this.writeDirect = writeDirect;
this.nonContext = nonContext || beanInfo.beanType.isEnum();
}
String Literal Equality 1
字符串比较应使用equals
字符串应使用equals()方法进行比较,而非’==’。
if ("$ref" == key && context != null) {
// 修改为"$ref".equals(key)
// ...
}
最后
该文章仅用于学习记录及交流,若有错误还望理解。