当前位置: 首页 > 文档资料 > Clojure 中文教程 >

异常处理(Exception Handling)

优质
小牛编辑
129浏览
2023-12-01

任何编程语言都需要Exception handling来处理运行时错误,以便可以维护应用程序的正常流程。 异常通常会破坏应用程序的正常流程,这就是我们需要在应用程序中使用异常处理的原因。

例外大致分为以下几类 -

  • Checked Exception - 除RuntimeException和Error之外的扩展Throwable类的类称为已检查异常。 例如IOException,SQLException等。在编译时检查已检查的异常。

让我们考虑以下程序对名为Example.txt的文件执行操作。 但是,总会出现文件Example.txt不存在的情况。

(ns clojure.examples.example
   (:gen-class))
;; This program displays Hello World
(defn Example []
   (def string1 (slurp "Example.txt"))
   (println string1))
(Example)

如果文件Example.txt不存在,则程序将生成以下异常。

Caused by: java.io.FileNotFoundException: Example.txt (No such file or
directory)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at clojure.java.io$fn__9185.invoke(io.clj:229)
at clojure.java.io$fn__9098$G__9091__9105.invoke(io.clj:69)
at clojure.java.io$fn__9197.invoke(io.clj:258)
at clojure.java.io$fn__9098$G__9091__9105.invoke(io.clj:69)

从上面的异常中,我们可以清楚地看到该程序引发了一个FileNotFoundException。

  • Unchecked Exception - 扩展RuntimeException的类称为未经检查的异常。 例如,ArithmeticException,NullPointerException,ArrayIndexOutOfBoundsException等。在编译时不检查未经检查的异常,而是在运行时检查它们。

一个经典案例是ArrayIndexOutOfBoundsException,当您尝试访问大于数组长度的数组索引时会发生这种情况。 以下是此类错误的典型示例。

(ns clojure.examples.example
   (:gen-class))
(defn Example []
   (try
      (aget (int-array [1 2 3]) 5)
      (catch Exception e (println (str "caught exception: " (.toString e))))
      (finally (println "This is our final block")))
   (println "Let's move on"))
(Example)

执行上述代码时,将引发以下异常。

caught exception: java.lang.ArrayIndexOutOfBoundsException: 5
This is our final block
Let's move on

Error

错误是无法恢复的,例如OutOfMemoryError,VirtualMachineError,AssertionError等。这些错误是程序永远无法从中恢复并导致程序崩溃的错误。 我们现在需要一些机制来捕获这些异常,以便在存在这些异常时程序可以继续运行。

下图显示了如何组织Clojure中的异常层次结构。 它都基于Java中定义的层次结构。

Clojure中的例外情况

捕捉异常

就像其他编程语言一样,Clojure提供了正常的“try-catch”块来捕获异常,以及它们何时发生。

以下是try-catch块的一般语法。

(try
   (//Protected code)
   catch Exception e1)
(//Catch block)

您可能引发异常的所有代码都放在Protected code block

catch block ,您可以编写自定义代码来处理异常,以便应用程序可以从异常中恢复。

让我们看看前面的例子,它生成了一个未找到文件的异常,看看我们如何使用try catch块来捕获程序引发的异常。

(ns clojure.examples.example
   (:gen-class))
(defn Example []
   (try
      (def string1 (slurp "Example.txt"))
      (println string1)
      (catch Exception e (println (str "caught exception: " (.getMessage e))))))
(Example)

上述程序产生以下输出。

caught exception: Example.txt (No such file or directory)

从上面的代码中,我们在try block包含了错误的代码。 在catch块中,我们只是捕获异常并输出发生异常的消息。 因此,我们现在有了一种有意义的方法来捕获由程序生成的异常。

多个捕获块

可以有多个catch块来处理多种类型的异常。 对于每个catch块,根据引发的异常类型,您将编写代码来相应地处理它。

让我们修改我们之前的代码以包含两个catch块,一个特定于我们的文件未找到异常,另一个用于一般异常块。

(ns clojure.examples.example
   (:gen-class))
(defn Example []
   (try
      (def string1 (slurp "Example.txt"))
      (println string1)
      (catch java.io.FileNotFoundException e (println (str "caught file
         exception: " (.getMessage e))))
      (catch Exception e (println (str "caught exception: " (.getMessage e)))))
   (println "Let's move on"))
(Example)

上述程序产生以下输出。

caught file exception: Example.txt (No such file or directory)
Let's move on

从上面的输出中,我们可以清楚地看到我们的异常是由'FileNotFoundException'catch块捕获的,而不是普通的。

最后座

finally块遵循try块或catch块。 无论发生异常,最终都会执行最后一段代码。

使用finally块允许您运行要执行的任何清理类型语句,无论受保护代码中发生什么。 以下是此块的语法。

(try
   (//Protected code)
   catch Exception e1)
(//Catch block)
(finally
   //Cleanup code)

让我们修改上面的代码并添加finally块代码。 以下是代码段。

(ns clojure.examples.example
   (:gen-class))
(defn Example []
   (try
      (def string1 (slurp "Example.txt"))
      (println string1)
      (catch java.io.FileNotFoundException e (println (str "caught file
         exception: " (.getMessage e))))
      (catch Exception e (println (str "caught exception: " (.getMessage e))))
      (finally (println "This is our final block")))
   (println "Let's move on"))
(Example)

上述程序产生以下输出。

caught file exception: Example.txt (No such file or directory)
This is our final block
Let's move on

从上面的程序中,您可以看到最后一个块也是在catch块捕获到所需的异常之后实现的。

由于Clojure从Java派生异常处理,类似于Java,因此Clojure中提供了以下方法来管理异常。

  • public String getMessage() - 返回有关已发生的异常的详细消息。 此消息在Throwable构造函数中初始化。

  • public Throwable getCause() - 返回由Throwable对象表示的异常的原因。

  • public String toString() - 返回与getMessage()结果连接的类的名称。

  • public void printStackTrace() - 将toString()的结果与堆栈跟踪一起打印到错误输出流System.err。

  • public StackTraceElement [] getStackTrace() - 返回包含堆栈跟踪上每个元素的数组。 索引0处的元素表示调用堆栈的顶部,而数组中的最后一个元素表示调用堆栈底部的方法。

  • public Throwable fillInStackTrace() - 使用当前堆栈跟踪填充此Throwable对象的堆栈跟踪,添加到堆栈跟踪中的任何先前信息。

以下是使用上面列出的一些方法的示例代码。

(ns clojure.examples.example
   (:gen-class))
(defn Example []
   (try
      (def string1 (slurp "Example.txt"))
      (println string1)
      (catch java.io.FileNotFoundException e (println (str "caught file
         exception: " (.toString e))))
      (catch Exception e (println (str "caught exception: " (.toString e))))
   (finally (println "This is our final block")))
   (println "Let's move on"))
(Example)

上述程序产生以下输出。

caught file exception: java.io.FileNotFoundException: Example.txt (No such file
or directory)
This is our final block
Let's move on