## 22.异常处理 在印度,法律不适用于富裕和政治阶层。 他们可以做任何事情并摆脱它。 尽管我们的法律规定贿赂是非法的,但几乎没有印度人没有贿赂。 自由言论和博客作者是无情的目标。 这里的一切都是例外,法律大多只是纸上写的。 对于腐败的富人和政客,我们可能无能为力,但至少我们可以在 Ruby 中处理异常! 让我在本节中解释如何在 Ruby 编程中处理异常。 让我们编写一个名为 [code / division_exception.rb](code/division_exception.rb) 的程序,该程序会因被零除而中断。 打开文本编辑器,在下面键入给定的代码并运行它 ```rb # division_exception.rb puts 67 / 0 ``` 输出量 ```rb division_exception.rb:3:in `/': divided by 0 (ZeroDivisionError) from division_exception.rb:3:in `
' ``` 如你所见,你将获得一个异常作为输出。 看到常数`ZeroDivisionError`。 我们将在几个示例中看到的使用。 因此,当 Ruby 解释器注意到它无法处理某些内容时,就会引发异常。 如果我们把例外扔给已经为开发一个程序付出了数百万美元的客户的客户,那将不是什么好事。 我们宁愿尝试提供他们可以理解的内容。 因此,在下面显示的示例 [code / rescue.rb](code/rescue.rb) 中,我们看到了如何处理此异常。 在文本编辑器中键入以下程序并运行它 ```rb # rescue.rb begin puts 67 / 0 rescue puts "We are unable to proceed due to unavoidable reasons :(" end ``` Output ```rb We are unable to proceed due to unavoidable reasons :( ``` 正如你看到的那样,你看到的不是友好的`ZeroDivisionError`输出,而是一条友好的消息,指出由于不可避免的原因其无法继续进行。 诀窍是,如果你认为在`begin`和`end`块周围的代码中可能会发生某些异常,如下所示 ```rb begin puts 67 / 0 end ``` 然后将出现异常时需要处理的代码放在一个名为`rescue`的关键字之后,如下所示 ```rb begin puts 67 / 0 rescue puts "We are unable to proceed due to unavoidable reasons :(" end ``` 发生异常时,`rescue`下面的代码开始执行。 而已! 好了,现在你如何粗略地处理异常。 现在让我们看一种捕获或从异常中解脱出来的改进方法。 参见下面的代码 [rescue_1.rb](code/rescue_1.rb) ,键入并执行 ```rb # rescue_1.rb begin puts 67 / 0 rescue ZeroDivisionError puts "Oh nooo! boom thata has cursed us!!!!!" end ``` Output ```rb Oh nooo! boom thata has cursed us!!!!! ``` 你会看到 Boom Thata(众神)诅咒的输出。 不必为此担心,Boom Thata 是我的朋友,并且会和他交谈以扭转局势。 在这里,我们将类似`rescue ZeroDivisionError`的代码放入其中,告诉它如果仅发生零除法错误,则可以进行挽救。 如果发生其他异常,由于我们仅处理`ZeroDivisionError`,你将不会因此而被救出。 为了显示我的意思,请在下面键入程序( [rescue_2.rb](code/rescue_2.rb) )并执行它 ```rb # rescue_2.rb begin "abc" * "def" rescue ZeroDivisionError puts "Oh nooo! boom thata has cursed us!!!!!" end ``` Output ```rb Traceback (most recent call last): 1: from rescue_2.rb:4:in `
' rescue_2.rb:4:in `*': no implicit conversion of String into Integer (TypeError) ``` 在这里,我们仍然在终端上出现错误,因为将两个字符串相乘会产生一个不同的错误,称为`TypeError`,如你在输出中看到的,而不是`ZeroDivisionError`。 并且在程序中,我们仅针对`ZerodivisionError`进行了救援。 假设你要打印出异常。 出于调试目的或其他目的。 那么该怎么做。 以下程序向你展示了这一点。 键入程序 printing_exception.rb 并运行它 ```rb # printing_exception.rb begin puts 67 / 0 rescue => e puts "The following exception has occured:" p e end ``` Output ```rb The following exception has occured: # ``` 如你所见,你可以打印一个异常。 只要注意`p e`行,它就是我们在其中打印异常的地方。 `e`是例外对象,`p`是看跌期权的简称。 希望你注意到此代码`rescue ⇒ e`将异常推送到变量`e`中。 `e`就是这种例外。 在下一个示例中,我们将看到如何追溯异常。 例外情况是,在现实世界中抛出的程序可能会被埋在多个层次中。 为了找到答案,你最好需要追溯它。 在文本编辑器中键入以下程序,然后运行它。 ```rb # backtracing_exception.rb begin puts 67 / 0 rescue => e p e.backtrace end ``` Output ```rb ["backtracing_exception.rb:4:in `/'", "backtracing_exception.rb:4:in `
'"] ``` 我们正在使用`p e.backtrace`打印反向跟踪。 如果你可以注意到输出,则表明第 4 行发生了异常。如果在文本编辑器中显示了行号,则可以立即识别该行并进行调试。 接下来(第二部分输出)表示 main 中发生了异常。 你可能想知道什么是主要的? 它在你的文本编辑器中首先运行的程序称为 main。 ### 22.1 异常和线程 我们已经在单线程的普通程序中看到了例外,但是 Ruby 是一种多线程编程语言。 让我们看看异常和线程如何混合和表现。 键入程序 [thread_exception_true.rb](code/thread_exception_true.rb) 并运行它 ```rb #!/usr/bin/ruby # thread_exception_true.rb t = Thread.new do i = 5 while i >= -1 sleep(1) puts 25 / i i -= 1 end end t.abort_on_exception = true sleep(10) puts "Program completed" ``` Output ```rb 5 6 8 12 25 thread_exception_true.rb:8:in `/': divided by 0 (ZeroDivisionError) from thread_exception_true.rb:8:in `block in
' ``` 如你所见,程序抛出`ZeroDivisionError`异常,当`i`的值变为零且需要除以 25 时,会在`while`循环中发生这种情况。 注意`t.abort_on_exception = true`行,这里我们告诉程序在出现异常时中止或停止。 如果所有其他线程并行运行,这将停止所有其他线程。 假设你有一个多线程程序,其中必须所有线程都必须无例外地运行,并且线程之间是相互依赖的,那么最好以这样的方式编写代码:当异常发生时程序中止 在其中一根线中凸起。 可以说,我们编写的程序可以忽略线程上的异常,而其他线程可以轻松运行,然后在 [thread_exception_false.rb](code/thread_exception_false.rb) 下看到程序中的`t.abort_on_exception = false`行。 在这里,我们指定`t.abort_on_exception = false`,以便程序运行,当发生异常时,特定线程停止运行,而其他线程继续运行,好像什么也没发生。 ```rb #!/usr/bin/ruby # thread_exception_false.rb t = Thread.new do i = 5 while i >= -1 sleep(1) puts 25 / i i -= 1 end end t.abort_on_exception = false sleep(10) puts "Program completed" ``` Output ```rb 5 6 8 12 25 Program completed ``` ### 22.2。 引发异常 我们已经看到了如何捕获异常并对其进行处理。 但是,如果我们想提出自己的例外情况怎么办? 在文本编辑器中键入程序 [raise.rb](code/raise.rb) 并执行它。 ```rb # raise.rb puts "Enter a number 1 - 10:" num = gets.to_i raise "You did not enter right num" unless (1..10).include? num ``` Output ```rb Enter a number 1 - 10: 25 raise.rb:5:in `
': You did not enter right num (RuntimeError) ``` 从输出中可以看到,如果输入的任何数字不是从 1 到 10,程序都会引发异常。请参见代码段`raise "You did not enter right num"`,这就是在 Ruby 中引发异常的全部过程。 关键字`raise`后跟一个对象,在这种情况下,我们给出了一个字符串,但是如果我们给出一个常量(引发异常的规范),那将是很好的。 [raise_1.rb](code/raise_1.rb) 下面的程序显示了如何处理自己的异常,该异常与你先前编写的救援程序没有什么不同。 ```rb # raise_1.rb def number_thing(num) raise "You did not enter right num" unless (1..10).include? num puts "You entered #{num} :)" end puts "Enter a number 1 - 10:" num = gets.to_i begin number_thing(num) rescue puts "You may have not entered number in valid range" end ``` Output ```rb Enter a number 1 - 10: 25 You may have not entered number in valid range ```