异常处理
在任何编程语言中都需要异常处理来处理运行时错误,从而可以保持应用程序的正常流程。 异常通常会破坏应用程序的正常流程,这就是为什么我们需要在我们的应用程序中使用异常处理的原因。
异常大致分为以下几类:
- 检查异常 - 扩展Throwable类(除了RuntimeException和Error)的类称为检查异常。 例如。 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。
- 未选中的异常 -扩展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
错误
错误是无法恢复的,比如: OutOfMemoryError,VirtualMachineError,AssertionError等。这些是程序永远不能恢复的错误,并将导致程序崩溃。 我们现在需要一些机制来捕获这些异常,以便如果这些异常存在,程序可以继续运行。
下图显示了如何组织Clojure中的异常层次结构。 它都基于Java中定义的层次结构。
捕捉异常
就像其他编程语言一样,Clojure提供了正常的“try-catch”块来捕获异常。
下面是Clojure try-catch语法。
(try
(//Protected code)
catch Exception e1)
(//Catch block)
所有可能引发异常的代码都放在受保护的代码块中。
在catch块中,您可以编写自定义代码来处理异常,以便应用程序可以从异常中恢复。
让我们看看我们之前的例子,它生成了一个未找到文件的异常,看看我们如何使用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块中包装错误的代码。 在catch块中,我们只是捕获我们的异常并输出一个异常已经发生的消息。 因此,我们现在有一个有意义的捕获异常的方法,它是由程序生成的。
多个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”捕获的。
finally块
finally块跟在try块或catch块之后。 代码的finally块总是执行,而不管异常的发生。
使用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