第七章 错误处理 “错误处理很重要,但如果它搞乱了代码逻辑,就是错误的做法”——大牛
1. 使用异常,而非返回码 返回码缺点:搞乱了调用者代码,调用者必须在调用之后即刻检查错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class DeviceController { ... public void sendShutDown () { DeviceHandle handle = getHandle(DEV1); if (handle != DeviceHandle.INVALID) { retrieveDeviceRecord(handle); if (record.getStatus() != DEVICE_SUSPENDED) { pauseDevice(handle); clearDeviceWorkQueue(handle); closeDevice(handle); } else { logger.log("Device suspended. Unable to shut down" ); } } else { logger.log("Invalid handle for: " + DEV1.toString()); } } ... }
抛异常优点:调用代码整洁,逻辑不会被错误处理搞乱;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class DeviceController { ... public void sendShutDown () { try { tryToShutDown(); } catch (DeviceShutDownError e) { logger.log(e); } } private void tryToShutDown () throws DeviceShutDownError { DeviceHandle handle = getHandle(DEV1); DeviceRecord record = retrieveDeviceRecord(handle); pauseDevice(handle); clearDeviceWorkQueue(handle); closeDevice(handle); } private DeviceHandle getHandle (DeviceID id) { ... throw new DeviceShutDownError ("Invalid handle for: " + id.toString()); ... } ... }
2. 先些 try-catch-finally 语句 3. 使用未检异常 已检异常的代价就是违反开放闭合原则:
4. 给出异常发生的环境说明 应创建信息充分的错误信息,并和异常一起传递出去,在消息中,应包括失败的操作和失败的类型。
5. 依调用者需要定义异常类 异常来源丰富:自身代码、其他组件、设备错误、网络错误、编程错误……
当我们在应用程序中定义异常类时,最重要的考虑应该是他们如何捕获 ;
例如:将第三方API打包(返回自己定义的异常)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ACMEPort port = new ACMEPort (12 );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" ); } finally { … }
打包类:LocalPort,获取并翻译ACMEPort类抛出的异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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 (ATM1212UnlockedException e) { throw new PortDeviceFailure (e); } catch (GMXError e) { throw new PortDeviceFailure (e); } }… }
6. 定义常规流程 特例模式:创建一个类或配置一个对象,用来处理特例。(异常行为被封装到特例对象中,客户端就不用应对异常行为了)
1 2 3 4 5 6 7 try { MealExpenses expenses = expenseReportDAO.getMeals(employee.getID()); m_total += expenses.getTotal(); } catch (MealExpensesNotFound e) { m_total += getMealPerDiem(); }
期望代码:不处理特殊情况
1 2 MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());m_total += expenses.getTotal();
创建特例对象:
1 2 3 4 5 public class PerDiemMealExpenses implements MealExpenses { public int getTotal () { } }
7. 别返回null值 如果你打算在方法中返回null值,不如
如果你在调用第三方API中可能返回null值的方法,考虑用新方法打包这个方法,然后
8. 别传递null值