6、对文件、IO、数据库等资源进行操作后没有及时、正确进行释放。
6.1、解读:
在使用文件、IO流、数据库连接等不会自动释放的资源时,应该在使用完毕后马上将其关闭。关闭资源的代码应该在try...catch...finally的finally内执行,否则可能造成资源无法释放。
6.2、示例:
- 错误案例如下:
public void writeProduct1(ProductServiceStruct product)
{
try
{
FileWriter fileWriter = new FileWriter("");
fileWriter.append(product.toString());
// 如果append()抛出异常,close()方法就不会执行,造成IO流长时间无法释放
fileWriter.close();
}
catch (IOException e)
{
...
}
}
- 关闭IO流的正确代码如下:
public void writeProduct2(ProductServiceStruct product)
{
FileWriter fileWriter = null;
try
{
fileWriter = new FileWriter("");
fileWriter.append(product.toString());
}
catch (IOException e)
{
...
//记录日志
}
finally
{
// 不管前面是否发生异常,finally中的代码一定会执行
if (fileWriter != null)
{
try
{
fileWriter.close();
}
catch (IOException e)
{
...
//记录日志
}
}
}
}
7、循环体编码时不考虑性能,循环体中包含不需要的重复逻辑。
7.1、解读:
循环体是软件中最容易造成性能问题的地方,所以在进行循环体编码时务必考虑性能问题。
在循环体内重复使用且不会变化的资源(如变量、文件对象、数据库连接等),应该在循环体开始前构造并初始化,避免在循环体内重复和构造初始化造成CPU资源的浪费。
除非业务场景需要,避免在循环体内构造try...catch块,因为每次进入、退出try...catch块都会消耗一定的CPU资源,将try...catch块放在循环体之外可以节省大量的执行时间。
7.2、示例:
public void addProducts(List<ProductServiceStruct> prodList)
{
for (ProductServiceStruct product : prodList)
{
// prodSrv在每次循环时都会重新获取,造成不必要的资源消耗
ProductService prodSrv = (ProductService) ServiceLocator.findService(ProductService.class);
// 避免在循环体内try...catch,放在循环体之外可以节省执行时间
try
{
prodSrv.addProduct(product);
}
catch (BMEException e)
{
...
//记录日志
}
}
}
注意:在循环体中遇到字符串相加,一定要使用StringBuffer这个类。
8、数据类没有重载toString()方法。
8.1、解读:
数据类如果没有重载toString()方法,在记录日志的时候会无法记录数据对象的属性值,给定位问题带来困难。
8.2、示例:
public class MdspProductExt
{
private String key;
private String value;
public String getKey()
{
return key;
}
public void setKey(String key)
{
this.key = key;
}
public String getValue()
{
return value;
}
public void setValue(String value)
{
this.value = value;
}
}
class BusinessProcess
{
private DebugLog log = LogFactory.getDebugLog(BusinessProcess.class);
public void doBusiness(MdspProductExt prodExt)
{
try
{
...
}
catch (PMSException e)
{
// MdspProductExt未重载toString()方法,日志中无法记录对象内属性的值,只能记录对象地址
log.error("error while process prodExt " + prodExt);
}
}
}
9、嵌套使用try-catch,或者try-catch后面没有必要的finally操作。
9.1、解读:
数据库操作、IO操作等需要使用结束close()的对象必须在try -catch-finally 的finally中close(),如果有多个IO对象需要close(),需要分别对每个对象的close()方法进行try-catch,防止一个IO对象关闭失败其他IO对象都未关闭。
9.2、示例:
/**
* <一句话功能简述>演示嵌套循环的错误
* <功能详细描述>
* @see [相关类/方法]
* @since [产品/模块版本]
*/
public class NestingTry
{
/**
* <一句话功能简述>
* <功能详细描述>
* @see [类、类#方法、类#成员]
*/
public void checkTry()
{
StringBuffer sb = null;
BufferedReader in = null;
File file = null;
try
{
initFtpClient(this.ftpClient, getFtpServConfig());
// 把绝对路径加入到文件中
String[] absoluteFiles null;
//错误:完全没有必要在内容进行异常处理,而应该将异常处理放在try块后面进行处理。
try
{
absoluteFiles = addAbsolutePath(this.ftpClient, files);
}
Catch(IOException ex)
{
Throw ex;
}
File file = null;
in = new BufferedReader(new FileReader(file));
sb = new StringBuffer();
}
catch (Exception e)
{
throw new FtpOperationsException(FtpConst.Ftp_Operations_Del_Error,
"IOException when invoking deleteFiles method", e);
}
//错误:IO资源没有正确释放,必须有finally进行释放资源
}
}
10、equals操作时没有将常量放在equals操作符的左边。
10.1、解读:
字符串变量与常量比较时,先写常量,这样可以避免空指针异常。
10.2、示例:
/**
* 定义常量
*/
public static final String SP_NAME = "SPNAME";
/**
* <一句话功能简述>常量比较
* <功能详细描述>
* @see [类、类#方法、类#成员]
*/
public void equalsConst()
{
String temp = null;
//错误;//在temp为null的情况下,此时会导致意想不到的异常抛出,如果将常量放在左边,就不会有这种问题。
if(temp.equals(SP_NAME))
{
temp = SP_NAME + "AA";
}
}