第七章 错误处理

第七章 错误处理

  • 错误处理不应该搞乱代码逻辑

7.1 使用异常而非返回码

  • 在以往的错误返回中,通常使用返回码(宏定义)、NULL等;在这样的操作往往需要调用者在调用后立即进行错误处理;但此操作极为容易被忽略,存在隐患。
  • 对于这种情况最好使用异常,隔离错误处理和业务逻辑;

7.2 先写Try-Catch-Finally

  • Try块和Catch块在程序中定义了一个范围;在Try块的代码中可以随时取消代码的执行,并在Catch块中继续;

  • 在构建代码逻辑的过程中,可以一步步实现功能;

    • 比如先写一个单元测试,实现单元功能;
    @Test(expected = StorageException.class)//单元测试,检测实现抛出的异常
    public void retrieveSectionShouldThrowOnInvalidFileName(){
      sectionStore.retrieveSection("invalid-file");
    }
    
    //部分实现功能,再在Try块中添加功能
    public List<RecordedGrip> retrieveSection(String sectionName){
      try{
        FileInputStream stream = new FileInputStream(sectionName);
        stream.close();
      }catch (FileNoFoundException e){
        throw new StorageException("retrieval error",e);
      }
      return new ArrayList<RecordedGrip>();
    }

7.3 使用不可控异常

  • 所谓可控异常就是在C++中的异常范围;
  • 不可控异常就是不使用异常范围;
  • 异常范围会导致调用有异常的函数申明可能出现的异常,如果该异常的处理程序在多级上层,那么每一层级的函数都要声明可能要出现的异常;这违反了开放闭合原则;
  • 对于一般应用的开发,可控异常(异常范围)成本要高于收益;

7.4 给出异常发生时的环境说明

  • 当抛出一个异常时,需要给出产生异常的原因和位置,以便修改错误;

7.5 依调用者需要定义异常类

ACMEPort port = new ACMEPort(12);

//该段代码中包含了较多的重复,并且由各种异常,对此可以对第三方API进行打包,使其返回相同的代码;
try{
  port.open();
}catch(DeviceResponseException e){
  reportPortError(e);
  logger.log("Device response exception",e);
}catch(ATM1212UnlockedException e){
  reportPortError(e);
  logger.log("Unlock exception",e);
}catch(GMXError e){
  reportPortError(e);
  logger.log("Device response exception",e);
}finally{
  ...
}

//以下方法对第三方API进行了打包,同一了向上throw的异常类型
LocalPort port = new LocalPort(12);
try{
  port.open()
}catch(PortDeviceFailure e){
  reportError(e);
  logger.log(e.getMessage e);
}finally{
  ...
}

public class LocalPort{
  private ACMEPort innerport;

  public LocalPort(int portNumber){
    innerPort = new ACMEPort(portNumber);
  }

  public void open(){
    try{
      innerPort.open();
    }catch(DeviceResponseException e){
      throw new PortDeviceFailure(e);
    }catch(ATM1212UnlockExcetion e){
      throw new PortDeviceFailure(e);
    }catch(GMXError e){
      throw new PortDeviceFailure(e);
    }
  }
  ...
}
  • 对一个第三方的API进行打包,降低了对它的依赖;

7.6 定义常规流程

 //此处打断了业务逻辑,消耗餐食和饭补是同一逻辑层的,将餐补放置异常处理,不符合逻辑
try{
  MealExpense expenses = expenseReportDAO.getMeals(employee.getID());
  m_total += expenses.getTotal();
}catch(MealExpenseNotFound e){ 
  m_total += getMealPerDiem();
}

//此处对于餐补的情况返回一个特例情况,这样就不用处理异常情了;
MealExpensses expenses = expenseReportDAO.getMeals(employee.getID());
m_total += expenses.getTotal();

public class PerDiemMealExpense implements MealExpenses{
  public int getTotal(){
    //retrun the per diem default
  }
}

7.7 别返回null值

  • 每一处可能返回null值就要进行返回值检查,这样的检查使得代码凌乱;
  • 对于返回null的函数最好可以返回特例,避免null判断;次之返回异常进行处理;

7.8 别传递null值

  • 除非API可以并且要求传递null值,否则不要;
  • 对于java,空对象的使用对抛出NullPointerException的异常,对此,如果检测到有null,定义一个新的异常,并编写异常处理函数;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值