1.异常类型

所有的异常分为4种类型,它们实际上是相互继承的类。

Throwable班级

所有异常的基类是类Throwable该类Throwable包含将当前调用堆栈(当前方法的堆栈跟踪)写入数组的代码。稍后我们将了解堆栈跟踪是什么。

throw运算符只能接受从Throwable类派生的对象。尽管理论上您可以编写类似 的代码throw new Throwable();,但通常没有人这样做。该类的主要目的Throwable是为所有异常提供一个父类。

Error班级

下一个异常类是Error类,直接继承类Throwable当出现严重问题时, Java 机器会创建该类Error(及其后代)的对象。例如,硬件故障、内存不足等。

通常,作为程序员,在程序中出现这样的错误(应该抛出 an 的那种)时,您无能为力:这些错误太严重了。Error您所能做的就是通知用户程序正在崩溃和/或将有关错误的所有已知信息写入程序日志。

Exception班级

ExceptionRuntimeException是针对许多方法运行中发生的常见错误。每个抛出的异常的目标是被知道如何正确处理它的块捕获。catch

当一个方法由于某种原因不能完成它的工作时,它应该立即通过抛出适当类型的异常来通知调用方法。

换句话说,如果一个变量等于null,该方法将抛出一个NullPointerException。如果将不正确的参数传递给该方法,它将抛出一个InvalidArgumentException. 如果该方法不小心被零除,它会抛出一个ArithmeticException.

RuntimeException班级

RuntimeExceptions是 的一个子集Exceptions。我们甚至可以说这RuntimeException是普通异常 ( ) 的轻量级版本Exception——对此类异常施加的要求和限制更少

Exception稍后您将了解 和 之间的区别RuntimeException


2. Throws:检查异常

所有 Java 异常都分为两类:已检查未检查

所有继承RuntimeExceptionor的异常Error都被认为是未经检查的异常 所有其他都是已检查的异常

重要的!

在引入检查异常 20 年后,几乎每个 Java 程序员都认为这是一个错误。在流行的现代框架中,95% 的异常都是未经检查的。几乎一模一样照抄Java的C#语言,并没有加入checked exceptions

已检查异常未检查异常之间的主要区别是什么?

对已检查的异常有额外的要求。粗略地说,它们是这些:

要求 1

如果一个方法抛出一个已检查的异常它必须在它的签名中指明异常的类型这样,调用它的每个方法都知道这个“有意义的异常”可能会发生在其中。

在关键字后的方法参数后面指明检查异常(不要误用关键字)。它看起来像这样:throwsthrow

type method (parameters) throws exception

例子:

检查异常 未经检查的异常
public void calculate(int n) throws Exception
{
   if (n == 0)
      throw new Exception("n is null!");
}
public void calculate(n)
{
   if (n == 0)
      throw new RuntimeException("n is null!");
}

在右边的例子中,我们的代码抛出了一个未经检查的异常——不需要额外的操作。 在左侧的示例中,该方法抛出一个已检查的异常,因此throws将关键字与异常类型一起添加到方法签名中。

如果一个方法希望抛出多个已检查的异常,则必须在关键字后指定所有这些异常throws,并以逗号分隔。顺序并不重要。例子:

public void calculate(int n) throws Exception, IOException
{
   if (n == 0)
      throw new Exception("n is null!");
   if (n == 1)
      throw new IOException("n is 1");
}

要求 2

如果您调用的方法在其签名中包含已检查的异常,则您不能忽略它抛出异常的事实。

您必须通过catch为每个异常添加块来捕获所有此类异常,或者将它们添加到您的方法的throws子句中。

就好像我们在说,“这些异常非常重要,我们必须捕获它们。如果我们不知道如何处理它们,那么必须通知任何可能调用我们方法的人,其中可能会发生此类异常。

例子:

想象一下,我们正在编写一种方法来创建一个由人类居住的世界。初始人数作为参数传递。所以如果人数太少,我们需要添加例外。

创造地球 笔记
public void createWorld(int n) throws EmptyWorldException, LonelyWorldException
{
   if (n == 0)
      throw new EmptyWorldException("There are no people!");
   if (n == 1)
      throw new LonelyWorldException ("There aren't enough people!");
   System.out.println("A wonderful world was created. Population: " + n);
}
该方法可能会抛出两个已检查的异常:

  • 空世界异常
  • 孤独世界异常

此方法调用可以通过 3 种方式处理:

1.不要捕获任何异常

当方法不知道如何正确处理这种情况时,通常会这样做。

代码 笔记
public void createPopulatedWorld(int population)
throws EmptyWorldException, LonelyWorldException
{
   createWorld(population);
}
调用方法不捕获异常并且必须将它们通知其他人:它将它们添加到自己的throws子句中

2.捕捉一些异常

我们处理我们可以处理的错误。但是我们不理解的,我们把它们丢给调用方法。为此,我们需要将他们的名字添加到 throws 子句中:

代码 笔记
public void createNonEmptyWorld(int population)
throws EmptyWorldException
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
}
调用者只捕获一个已检查的异常—— LonelyWorldException. 另一个异常必须添加到它的签名中,在throws关键字之后表明它

3.捕获所有异常

如果该方法没有向调用方法抛出异常,那么调用方法总是确信一切正常。并且它将无法采取任何行动来修复异常情况。

代码 笔记
public void createAnyWorld(int population)
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
   catch (EmptyWorldException e)
   {
      e.printStackTrace();
   }
}
在此方法中捕获所有异常。来电者将确信一切顺利。


3.捕获多个异常

程序员真的很讨厌重复代码。他们甚至提出了相应的开发原则——DRY Don't Repeat Yourself。但是在处理异常时,经常会出现一个try块后面跟着几个catch具有相同代码的块的情况。

或者可能有 3 个catch块具有相同的代码,另外 2 个catch块具有其他相同的代码。当您的项目负责任地处理异常时,这是一种标准情况。

从版本 7 开始,在 Java 语言中添加了在单个块中指定多种异常类型的功能catch。它看起来大致是这样的:

try
{
   // Code where an exception might occur
}
catch (ExceptionType1 | ExceptionType2 | ExceptionType3 name)
{
   // Exception handling code
}

catch您可以拥有任意数量的块。但是,单个catch块不能指定相互继承的异常。也就是说不能写catch( Exception| RuntimeExceptione),因为RuntimeException类继承了Exception