要點簡介
- 為什么要使用異常而不是返回碼(if-else)
- 主流語言對待受檢異常和非受檢異常態(tài)度和處理方式
- 拋出異常的注意事項
- 如果不想使用異常(不打斷流程)應該怎么處理
- null值的處理
一、判斷和異常的取舍問題
剛寫Java代碼時,我也有一個疑問,就是為什么要有異常這個東西。比如要訪問一個文件,如果文件不存在完全可以通過if-else進行判斷。
現(xiàn)在,其實有了更深的理解。就是簡單的if-else其實也可以。但是在復雜情況下,如:代碼段7.1,有很多狀態(tài)需要判斷的時候,代碼就會顯得雜亂無章。
正常情況下,if-else應該用于表現(xiàn)程序邏輯,業(yè)務代碼。但是按照下面的寫法,業(yè)務代碼和錯誤判斷(包含很多前置檢查)就有可能混為一談。不好區(qū)分。
代碼段7.1
public class DeviceController {
public void sendShutDown() {
DeviceHandle handle = getHandle(DEVl);
// Check the state of the device
if (handle != DeviceHandle.INVALID) {
// Save the device status to the record field
retrieveDeviceRecord(handle);
// If not suspended, shut down
if (record.getStatus != DEVICE.SUSPENDED) {
pauseDevice(handle);
} else {
logger.log("Device suspended.Unable_to_shut_down" );
}
} else {
logger.log("Invalid handle for: " + DEVI.toStringO);
}
}
}
所以,下面 代碼段7.2 所示就會更簡潔和優(yōu)雅.異常檢查和業(yè)務邏輯相互獨立.
代碼段7.2
public class DeviceController {
public void sendShutDown() {
try {
tryToShutDown();
/*以及很多業(yè)務代碼*/
} catch (DeviceShutDownError e) {
logger.log(e);
} catch (OtherError e) {
logger.log(e);
}
}
}
二、推薦使用不可控異常(unchecked Exception)
目前,C#、C++、Python、Ruby都不再(或從未)支持或受檢異常。因為其違反了開放\閉合原則,即如果在某層次方法里面拋出了受檢異常,那么其上層除非catch住,否則都要重重拋出此異常。完全的耦合了。
所以現(xiàn)在的建議是:
一般應用開發(fā)僅考慮拋出unchecked Exception,關鍵代碼庫,則可考慮checked Exception
三、拋出異常的注意事項
你拋出的每個異常,都應當提供足夠的環(huán)境說明,以便判斷錯誤的來源和處所。在Java中,你可以從任何異常里得到堆棧蹤跡(stacktrace);然而,堆棧蹤跡卻無法告訴你該失敗操作的初衷。應創(chuàng)建信息充分的錯誤消息,并和異常一起傳遞出去。在消息中,包括失敗的操作和失敗類型。如果你的應用程序有日志系統(tǒng),傳遞足夠的信息給catch塊,并記錄下來。
另外,補充一點,拋出異常之前,應該記錄日志,否則你可能無法找到異常的拋出地點。如代碼段7.3:
代碼段7.3
if (record.getStatus != DEVICE.SUSPENDED) {
log.error("原因-異常");
throw new BusinessException("message","Detail");
}
四、可不可以不使用異常呢
五、不要返回和傳遞null值
5.1不要返回null值
不要返回null值的原因:
(1)返回null值會導致程序中充滿對null的判斷;
(2)如果不注意對null的判斷,則會導致很不友好的空指針異常;
針對這個問題也搜了一下,辯證的看到處理方法:
(1)對于集合和數(shù)組作為返回值,使用長度為零的數(shù)組或者空集合,而不是null;
(2)字符串作為返回值,使用空字符串來代替null;
(3)當空對象與其他返回對象有一樣的行為和意義時,使用空對象;
關于這一條的理解目前,我還沒理解到,這里暫時做一個標記,期望后續(xù)可以理解
例如,有一個方法返回一個迭代器。一個空的迭代器可以定義為NullIterator并實現(xiàn)Iterator接口,他的next方法永遠返回null,他的hasNext方法永遠返回false。這樣,使用這個方法返回值的代碼就不要進行null判斷,因為NullIterator的行為與其他迭代器一樣。
(4)針對這個規(guī)則的例外任何在邏輯上表示查找(search或者get)的意思時,應該返回null;代碼段7.4 說明了為什么應該例外處理;
代碼段7.4
User getUserById(String id){
User user = dao.getUserById(id);
if(user == null)
return new User();
return user;
}
5.2不要傳遞null值
不傳遞null值的意思,即不要在方法調用時,將參數(shù)設置為null。這將會帶來更不可預料的后果。
但是如果程序中如果卻需要對null值進行判斷,可以拋出InvalidArgumentException或者使用斷言來處理(而不是僅僅忽略null值)。
代碼段7.5
public double xProjection(Point p1, Point p2) {
if (p1 == null || p2 == null) {
throw InvalidArgumentException ("Invalid argument for MetricsCalculator.xProjection");
}
return (p2.x - pl.x) * 1.5;
}
}