內建控制結構之使用try表達式處理異常

拋出異常
異常的拋出看上去與Java如出一轍,首先建立一個異常對象而後用throw關鍵字拋出。但在scala裏,throw也是有結果類型的表達式。下面舉個有關結果類型的例子:java

package scalaTest
object Test6 {
    def main(args:Array[String]):Unit = {
        println(fun(9))
    }
    def fun(n:Int) = {
        if(n % 2 == 0) n/2
        else throw new RuntimeException("n must be even")
    }
}

執行結果:程序員

這段代碼的意思是,若是n是偶數,將打印n的一半。若是n不是偶數,那麼異常將被拋出。所以,不管怎麼說,把拋出的異常看成任何類型的值都是安全的。任何使用經throw返回值的嘗試都不會起做用,所以這樣作不會有害處
從技術角度上來講,拋出異常的類型是Nothing。儘管throw不實際產生任何值,你仍是能夠把它看成表達式。這種小技巧或許看上去很怪異,但像在上面這樣的例子裏卻經常頗有用。If的一個分支計算值,另外一個拋出異常並獲得Nothing。整個if表達式的類型就是那個實際計算值的分支的類型(Nothing類型將在之後會講到)。數據庫


捕獲異常安全

package scalaTest

import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException

object Test6 {
    def main(args:Array[String]):Unit = {
        try {
            val f = new FileReader("input.txt")
            //使用並關閉文件,省略....................
        }catch{
            case ex:FileNotFoundException => {
                println("文件沒有找到!")
            }
            case ex:IOException => println("IO異常")
        }
    }
}

上面演示了捕獲異常的語法。選擇catch子句這種語法的緣由是爲了與scala很重要的部分:模式匹配保持一致,模式匹配是一種很強大的特徵(咱們在後面章節中會講到)。
這個try-catch表達式的處理方式與其餘語言中的異常處理一致。首先執行程序體,若是拋出異常,則依次嘗試每一個catch子句。本例中,若是異常是FileNotFoundException,那麼第一個子句將被執行。若是是IOException類型,第二個子句將被執行。若是都不是,那麼try-catch將終結把異常向上拋出去。
注意
你將很快發現與java的差異是scala裏不須要捕獲檢查異常,或把它們聲明在throws子句中。若是願意,你能夠用@throws註解聲明throws子句,但這不是必須的(這個後面會講到)。函數


finally子句
若是想讓某些代碼不管方法如何停止都要執行的話,能夠把表達式放在finally子句裏。如例:測試

package scalaTest

import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException

object Test6{
    def main(args:Array[String]):Unit = {
        try{
           val f = new FileReader("input.txt")
            //省略.......
        }catch{
            case ex:FileNotFoundException => {
                println("文件沒有找到")
            }
            case ex:IOException => println("IO異常")
        }finally{
            println("必須執行的語句")
        }
    }
}

或者url

package scalaTest

import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException

object Test6{
    def main(args:Array[String]):Unit = {
        try{
            val f = new FileReader("./.classpath")
            println(f.getEncoding())
            //省略...........
        }finally{
            println("必須執行的語句")
        }
    }
}

注意:通常確保內存資源,如:文件套接字數據庫鏈接等被關閉的慣例方式在finally中進行處理,這跟在java中是同樣的。Scala裏還可使用另外一種被稱爲出借模式(loan pattern)的技巧更簡潔地達到一樣的目的(這個出借模式後面再講)。spa


生成值
和其餘大多數scala控制結構同樣,try-catch-finally也產生值。如例:.net

package scalaTest

import java.net.URL
import java.net.MalformedURLException

object Test6 {
    def main(args:Array[String]):Unit = {
        println(urlFor("ddd"))
    }
    def urlFor(path:String) = {
        try{
            new URL(path)
        }catch{
            case e:MalformedURLException => new URL("http://www.scala-lang.org")
        }
    }
}

結果:scala

上例演示瞭如未嘗試拆分URL,但若是URL格式錯誤就使用默認值。也就是,若是沒有異常拋出,則對應於try子句;若是拋出異常並被捕獲,則對應於相應的catch子句。若是異常被拋出但沒被捕獲,表達式就沒有返回值
由finally子句計算獲得的值,即便有也會被拋棄一般finally子句作一些諸如關閉文件之類的清理工做,它們不該該修改主函數體或catch子句中計算的值。
Scala的行爲與java的差異僅在於java的try-finally不產生值。在java裏,若是finally子句包含返回語句,或拋出一個異常,這個返回值或異常將「凌駕」於任何以前在try代碼塊或某個catch子句裏產生的值或異常之上。
咱們來測試一下,scala的finally中定義返回語句的狀況,以下例:

package scalaTest
object Test6{
    def main(args:Array[String]):Unit = {
        println(f)
    }
    def f() = {
        try{1}finally{2}
    }
}

結果:

咱們再來看看另外一個很特別的例子

package scalaTest
object Test6 {
    def main(args:Array[String]):Unit = {
        println(f)
    }
    def f():Int = {
        try{return 1}finally{return 2}
    }
}

(注: scala中使用return進行返回值的時候,方法上必定要註明返回類型!!!),咱們再來看看結果(很震驚!!!!!!至關震驚!!!!):


這兩個例子足以令大多數程序員震驚!!!所以一般最好仍是避免用finally子句返回值,而是把它理解爲確保某些操做發生的途徑,如關閉打開文件。

相關文章
相關標籤/搜索