1.异常类型
所有的异常分为4种类型,它们实际上是相互继承的类。
Throwable
班级
所有异常的基类是类Throwable
。该类Throwable
包含将当前调用堆栈(当前方法的堆栈跟踪)写入数组的代码。稍后我们将了解堆栈跟踪是什么。
throw运算符只能接受从Throwable
类派生的对象。尽管理论上您可以编写类似 的代码throw new Throwable();
,但通常没有人这样做。该类的主要目的Throwable
是为所有异常提供一个父类。
Error
班级
下一个异常类是Error
类,直接继承类Throwable
。当出现严重问题时, Java 机器会创建该类Error
(及其后代)的对象。例如,硬件故障、内存不足等。
通常,作为程序员,在程序中出现这样的错误(应该抛出 an 的那种)时,您无能为力:这些错误太严重了。Error
您所能做的就是通知用户程序正在崩溃和/或将有关错误的所有已知信息写入程序日志。
Exception
班级
和Exception
类RuntimeException
是针对许多方法运行中发生的常见错误。每个抛出的异常的目标是被知道如何正确处理它的块捕获。catch
当一个方法由于某种原因不能完成它的工作时,它应该立即通过抛出适当类型的异常来通知调用方法。
换句话说,如果一个变量等于null
,该方法将抛出一个NullPointerException
。如果将不正确的参数传递给该方法,它将抛出一个InvalidArgumentException
. 如果该方法不小心被零除,它会抛出一个ArithmeticException
.
RuntimeException
班级
RuntimeExceptions
是 的一个子集Exceptions
。我们甚至可以说这RuntimeException
是普通异常 ( ) 的轻量级版本Exception
——对此类异常施加的要求和限制更少
Exception
稍后您将了解 和 之间的区别RuntimeException
。
2. Throws
:检查异常
所有 Java 异常都分为两类:已检查和未检查。
所有继承RuntimeException
or的异常Error
都被认为是未经检查的异常。 所有其他都是已检查的异常。
在引入检查异常 20 年后,几乎每个 Java 程序员都认为这是一个错误。在流行的现代框架中,95% 的异常都是未经检查的。几乎一模一样照抄Java的C#语言,并没有加入checked exceptions。
已检查异常和未检查异常之间的主要区别是什么?
对已检查的异常有额外的要求。粗略地说,它们是这些:
要求 1
如果一个方法抛出一个已检查的异常,它必须在它的签名中指明异常的类型。这样,调用它的每个方法都知道这个“有意义的异常”可能会发生在其中。
在关键字后的方法参数后面指明检查异常(不要误用关键字)。它看起来像这样:throws
throw
type method (parameters) throws exception
例子:
检查异常 | 未经检查的异常 |
---|---|
|
|
在右边的例子中,我们的代码抛出了一个未经检查的异常——不需要额外的操作。 在左侧的示例中,该方法抛出一个已检查的异常,因此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
子句中。
就好像我们在说,“这些异常非常重要,我们必须捕获它们。如果我们不知道如何处理它们,那么必须通知任何可能调用我们方法的人,其中可能会发生此类异常。
例子:
想象一下,我们正在编写一种方法来创建一个由人类居住的世界。初始人数作为参数传递。所以如果人数太少,我们需要添加例外。
创造地球 | 笔记 |
---|---|
|
该方法可能会抛出两个已检查的异常:
|
此方法调用可以通过 3 种方式处理:
1.不要捕获任何异常
当方法不知道如何正确处理这种情况时,通常会这样做。
代码 | 笔记 |
---|---|
|
调用方法不捕获异常并且必须将它们通知其他人:它将它们添加到自己的throws 子句中 |
2.捕捉一些异常
我们处理我们可以处理的错误。但是我们不理解的,我们把它们丢给调用方法。为此,我们需要将他们的名字添加到 throws 子句中:
代码 | 笔记 |
---|---|
|
调用者只捕获一个已检查的异常—— LonelyWorldException . 另一个异常必须添加到它的签名中,在throws 关键字之后表明它 |
3.捕获所有异常
如果该方法没有向调用方法抛出异常,那么调用方法总是确信一切正常。并且它将无法采取任何行动来修复异常情况。
代码 | 笔记 |
---|---|
|
在此方法中捕获所有异常。来电者将确信一切顺利。 |
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
| RuntimeException
e),因为RuntimeException
类继承了Exception
。
GO TO FULL VERSION