内建控制结构之使用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子句返回值,而是把它理解为确保某些操做发生的途径,如关闭打开文件。

相关文章
相关标签/搜索