1. 例外情况

>

终于,程序员想到了标准化和自动化错误处理。这发生在异常被发明的时候。现在异常机制处理了80%的异常情况。

如果某个学者提出例外,那很可能是他或她的博士论文的主题。如果一个程序员想到了它,那么他可能会从同事那里得到一个友好的拍拍:“看起来不错,兄弟。”

当 Java 程序中出现错误时,例如除法0,会发生一些奇妙的事情:

步骤1

创建一个特殊的异常对象,其中包含有关发生的错误的信息。

Java 中的一切都是对象,异常也不例外🙂Exception 对象有自己的类,唯一区别于普通类的是它们继承了类Throwable

第二步

异常对象被“抛出”。也许这里的措辞会更好。“抛出异常”更像是触发火警或发出“DEFCON 1”警报。

当 Java 机器抛出异常时,程序的正常运行停止,“紧急协议”开始。

第三步

抛出异常的方法立即退出。异常被传递给调用方法,该方法也立即退出。依此类推,直到main方法退出。当main方法终止时,程序也终止。

例子:

代码 控制台输出
class Solution
{
   public static void main(String[] args)
   {
      System.out.println("Your attention, please! Preparing for the end of the world");
      endTheWorld();
      System.out.println("The world ended successfully");
   }

   public static void endTheWorld()
   {
      System.out.println("We're doing something important");
      doSomeWork(0);
      System.out.println("Everything is working well");
   }

   public static void doSomeWork(int n)
   {
      System.out.println("Nothing terrible will happen: " + n);
      System.out.println(2 / n);
      System.out.println("Nothing terrible happened: " + n);
   }
}
Your attention, please! Preparing for the end of the world
We're doing something important
Nothing terrible will happen: 0

第 20 行发生异常:除以 0。Java机器立即创建异常——一个ArithmeticException对象并将其“抛”给方法。

方法divide()立即结束,所以我们永远不会看到字符串:Nothing terrible happened: 0。程序返回到方法endTheWorld(),情况重复:系统中出现未处理的异常,这意味着方法endTheWorld()也异常终止。然后该main方法终止,程序停止。

这些例外的目的是什么?那么,您可以编写自己的代码来捕获特定类型的异常,并编写自己的逻辑来处理异常情况。


2.捕捉异常:try-catch

Java 有一个异常捕获机制,可以让你停止这种异常终止的方法。它看起来像这样:

try
{
   // Code where an exception might occur
}
catch(ExceptionType name)
{
   // Exception handling code
}

这种构造称为try-catch块。

可能发生异常的代码用大括号括起来,前面有单词try

在大括号之后,我们有关键字catch括号内是异常变量的声明。其后是花括号,如果发生指定类型的异常,则将要执行的代码包裹起来

如果在执行“ primary code ”的过程中没有抛出异常,那么catch块里面的代码就不会被执行。如果发生异常,则为(如果抛出异常的类型与括号中变量的类型相同)。

例子:

代码 控制台输出
class Solution
{
   public static void main(String[] args)
   {
      System.out.println("Hadron Collider launched");

      try
      {
         launchHadronCollider(1);
         launchHadronCollider(0);
      }
      catch(Exception e)
      {
         System.out.println("Error! Caught an exception");
         System.out.println("The planet was sucked into a black hole!");
      }

      System.out.println("The Hadron Collider stopped");
   }

   public static void launchHadronCollider(int n)
   {
      System.out.println("Everything is working well: " + n);
      System.out.println(2/n);
      System.out.println("There are no problems: " + n);
   }
}
Hadron Collider launched
Everything is working fine: 1
2
There are no problems: 1
Everything is working fine: 0
Error! Caught an exception
The planet has been sucked into a black hole!
The Hadron Collider is stopped


3.多catch

多个 catch 块

理论上,可以在一段代码中抛出各种异常。有些你想用一种方式处理,有些你想用另一种方式处理,还有一些你会决定根本不处理。

Java 开发人员决定帮助您解决问题,让您catch在一个块之后编写多个块而不是一个块try

try
{
   // Code where an exception might occur
}
catch (ExceptionType1 name1)
{
   // Code for handling ExceptionType1
}
catch (ExceptionType2 name2)
{
   // Code for handling ExceptionType2
}
   catch (ExceptionType3 name3)
{
   // Code for handling ExceptionType3
}

例子:

代码 控制台输出
class Solution
{
   public static void main(String[] args)
   {
      System.out.println("Start of main method");
      try
      {
         calculate(0);
      }
      catch (ArithmeticException e)
      {
         System.out.println("Division by 0");
      }
      catch(Exception e)
      {
         System.out.println("Caught some kind of exception");
      }

      System.out.println("End of main method");
   }

   public static void calculate(int n)
   {
      System.out.println("Start of calculate method: " + n);
      System.out.println(2/n);
      System.out.println("End of calculate method: " + n);
   }
}
Start of main method
Start of calculate method: 0
Division by 0
End of main method


catch4.块的顺序

块中发生的异常try只能由单个块捕获catch。您不能有来自多个块的代码被执行的异常处理情况。catch

但是块的顺序很重要。

您可能会遇到异常可能被多个块捕获的情况。如果是这种情况,那么异常将被最先出现的catch块(最接近该try块)捕获。

怎么会出现多个catch块都能捕获到同一个异常的情况呢?

所有异常都属于一个单一的继承层次结构——见图表。

异常层次结构

ArithmeticException可以将一个对象分配给一个变量,该变量的类型是ArithmeticException或其任何祖先类: RuntimeException ,ExceptionThrowable— 参见图表。

我们将在第 21 关详细讨论继承和祖先类。

这段代码将编译得很好:

继承的好处:
ArithmeticException ae    = new ArithmeticException();
RuntimeException runtime  = new ArithmeticException();
Exception exception       = new ArithmeticException();
Throwable trwbl           = new ArithmeticException();

ArithmeticException所以你可以用上面 4 个块中的任何一个来捕获 an catch

示例 1:

代码 控制台输出
class Solution
{
   public static void main(String[] args)
   {
      System.out.println("Start of main method");
      try
      {
         calculate(0);
      }
      catch(ArithmeticException e)
      {
         System.out.println("Division by 0");
      }
      catch(Exception e)
      {
         System.out.println("Caught some kind of exception");
      }

      System.out.println("End of main method");
   }

   public static void calculate(int n)
   {
      System.out.println("Start of calculate method: " + n);
      System.out.println(2/n);
      System.out.println("End of calculate method: " + n);
   }
}
Start of main method
Start of calculate method: 0
Division by 0
End of main method

在这个例子中, the可以被 the和blocksArithmeticException捕获。它会被距离该区块最近的区块——第一个区块——捕获。catch (Exception e)catch (ArithmeticException e)trycatch

为避免意外,最好将catch几乎可以捕获所有异常的块放在块列表的末尾附近catch

Throwable类型通常能够捕获 Java 中的所有可能的异常。如果将它放在第一个catch块中,则代码将无法编译,因为编译器知道存在无法访问的代码块。