Java十大低级错误总结(二)

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";

        }
    }

猜你喜欢

转载自blog.csdn.net/MyronCham/article/details/82854765