Overview
本章主要介紹各語言中異常聲明和處理方式。
Java 篇
分類
在 Java 中異常是種特殊對(duì)象,可以分為檢查異常和非檢查異常,所有 RuntimeException 都屬于非檢查異常。
非檢查異常表示在編寫程序時(shí)就應(yīng)該避免的錯(cuò)誤(例:除以 0),在程序完成并正常運(yùn)行時(shí)不應(yīng)該發(fā)生,所以無需對(duì)非檢查異常進(jìn)行額外的檢查。
例:
int i = 5/0;
檢查異常屬于程序正常運(yùn)行時(shí)也可能會(huì)發(fā)生的異常(例:讀取的文件被刪除了),Java 要求必須對(duì)檢查異常手動(dòng)進(jìn)行 try..catch 或者通過方法聲明向上拋出。
處理異常
拋出異常
通過方法聲明拋出異常
public void test() throws MyException, FileNotFoundException {
}
手動(dòng)拋出異常
throw new FileNotFoundException("File not found.");
一般來說實(shí)際編程時(shí)通??梢栽诿總€(gè)方法中都使用方法聲明拋出異常,然后在最上層使用 try..catch 捕獲異常
try…catch...finally
try {
// do something
} catch (MyException e) {
} catch (FileNotFoundException e) {
} finally {}
其中無論異常是否會(huì)發(fā)生,finally 的代碼塊總是會(huì)被執(zhí)行。
在 Java 1.7 后以上代碼還可以簡(jiǎn)寫為
try {
// do something
} catch (MyException | FileNotFoundException e) {
} finally {}
回調(diào)中的異常捕獲
Java 這種處理方式有時(shí)會(huì)讓程序變得非常啰嗦,無法看清實(shí)際的業(yè)務(wù)邏輯,如果遇到回調(diào)的話就更是如此
例:
class MyBean {
interface Callbacks {
void done();
}
public void when(Callbacks callbacks) {
callbacks.done();
}
}
try {
new MyBean().when(() -> {
try {
} catch (MyException | FileNotFoundException e) {
System.out.println("Some errors occur in callback.");
}
});
} catch (FileNotFoundException | MyException e) {
System.out.println("Some errors occur.");
}
以上可以看到由于 try..catch 無法捕獲回調(diào)中的異常,所以回調(diào)外和回調(diào)內(nèi)部都要編寫 try..catch 語句,如果程序邏輯或者回調(diào)更加復(fù)雜的話程序簡(jiǎn)直毫無可讀性。
從目前其它 JVM 語言都移除了檢查異常來看,Java 的檢查異常已經(jīng)屬于一個(gè)語言上的缺點(diǎn)了。強(qiáng)制的檢查異常導(dǎo)致了 Java 應(yīng)用中充斥了大量的防御性編程,這其中相當(dāng)數(shù)量的防御性方式都是馬其頓防線,起不了任何作用。然而與其對(duì)應(yīng)的仍其奔潰編程又過于激進(jìn),而且與 Java 的相性不是太好。所以使用 Java 恐怕在很長(zhǎng)一段時(shí)間內(nèi)都要繼續(xù)忍受這種糟粕。
Groovy 篇
分類
Groovy 中所有異常均為非檢查異常,所以無需添加額外的方法聲明。
處理異常
拋出異常
當(dāng)然如果希望的話,也可以像 Java 一樣通過方法聲明拋出異常
def test() throws MyException {}
手動(dòng)拋出異常
throw new FileNotFoundException("File not found.")
try…catch...finally
try {
// do something
} catch (MyException | FileNotFoundException e) {}
} finally {}
回調(diào)中的異常捕獲
同 Java 一樣當(dāng)遇到回調(diào)時(shí) try..catch 語句會(huì)變得非常難看,不過在 Groovy 中可以通過使用閉包來解決這個(gè)問題
class MyBean {
interface Callbacks {
def done()
}
def when(Callbacks callbacks) {
tryCatchClosure {
callbacks.done()
}
}
def tryCatchClosure(Closure closure) {
try {
closure();
} catch (MyException | FileNotFoundException e) {
println("Some errors occur in closure.")
}
}
}
try {
uncheckedException()
checkedException()
new MyBean().when(new MyBean.Callbacks() {
@Override
def done() {
checkedException()
}
})
} catch (ArithmeticException | FileNotFoundException | MyException e) {
println("Some errors occur.")
}
Scala 篇
分類
Scala 中所有異常也都為非檢查異常。
處理異常
拋出異常
盡管沒有必要,如果你希望的話,也可以通過注解聲明拋出異常
@throws(classOf[MyException])
def test(): Unit = {
}
手動(dòng)拋出異常
throw new FileNotFoundException("File not found.")
try...catch...finally
try {
// do something
} catch {
case e: MyException =>
case e: FileNotFoundException =>
} finally {}
回調(diào)中的異常捕獲
同 Groovy 一樣,Scala 中也可以通過閉包來處理回調(diào)中的異常
class MyBean {
def when(callback: () => Unit): Unit = {
try {
callback()
} catch {
case _: Throwable => println("Some errors occur in closure.")
}
}
}
try {
checkedException()
new MyBean().when2(() => {
checkedException()
})
} catch {
case _: Exception => println("Some errors occur.")
} finally {}
在 Scala 中 throw 異常也是由返回值的,并且返回值為 Nothing,所以可以寫出以下語句
val n = 4
val half =
if (n % 2 == 0)
n / 2
else
throw new RuntimeException("n must be even")
println(half) // 2
或者在 try..catch 中處理返回值
val m = try {
"99".toInt
} catch {
case e: Exception => -99
}
println(m)
但是需要注意的是不要在 finally 中處理返回值,這是一種很糟的處理方式,會(huì)引發(fā)不可預(yù)知的錯(cuò)誤,以下例子可以證明這一點(diǎn)
def f(): Int = try {
return 1
} finally {
return 2
}
def g(): Int = try {
1
} finally {
2
}
def q(): Int = try {
return 1
} finally {
}
println(f()) // 2
println(g()) // 1
println(q()) // 1
Kotlin 篇
分類
Kotlin 中所有異常也都為非檢查異常。
處理異常
盡管沒有必要,如果你希望的話,也可以通過注解聲明拋出異常
@Throws(MyException::class, FileNotFoundException::class)
fun test() {
}
手動(dòng)拋出異常
throw FileNotFoundException("File not found.")
try...catch...finally
try {
// do something
} catch(e: FileNotFoundException) {
} catch(e: MyException) {
} finally {}
回調(diào)中的異常捕獲
同 Scala 一樣,Kotlin 中也可以通過閉包來處理回調(diào)中的異常
class MyBean {
fun callback(callback: () -> Unit) {
try {
callback()
} catch(e: FileNotFoundException) {
println("Some errors occur in closure.")
} catch(e: MyException) {
println("Some errors occur in closure.")
}
}
}
try {
checkedException()
new MyBean().callback {
checkedException()
})
} catch(e: Exception) {
println("Some errors occur.")
} finally {}
在 Kotlin 中同 Scala 一樣 throw 異常也是有返回值的
val n = 4
val half =
if (n % 2 == 0)
n / 2
else
throw new RuntimeException("n must be even")
println(half) // 2
或者在 try..catch 中處理返回值
val m = try {
"99".toInt()
} catch(e: Exception) {
-99
}
println(m)
Kotlin 中并不允許直接在 try..catch..finally 中使用 return 語句,但是還是會(huì)發(fā)生難以預(yù)料的錯(cuò)誤
fun f2(): Int {
try {
return 1
} finally {
return 2
}
}
fun g(): Int = try {
1
} finally {
2
}
println(f2()) // 2
println(g()) // 1
Summary
- 除了 Java 外,其它語言都可以使用閉包來處理回調(diào)中的異常
- 只有 Java 擁有檢查異常
- Scala 的 try..catch 語句與其它語言都不一樣
- Scala 和 Kotlin 中 try..catch 都是表達(dá)式,所以可以有返回結(jié)果
文章源碼見 https://github.com/SidneyXu/JGSK 倉(cāng)庫(kù)的 _21_exception 小節(jié)