你好!在上一课中,我们熟悉了 Java 语言中的异常,并看到了如何使用它们的示例。今天我们将深入了解异常的结构,并学习如何编写我们自己的异常 :)
所有异常在
所有已检查的异常都派生自该类
异常类型
正如我们之前所说,Java 中的异常非常多,差不多有 400 个!但是它们都被分成几组,所以很容易记住它们。它看起来是这样的:
Throwable
类中都有一个共同的祖先。从中派生出两大类:异常(Exception)和错误(Error)。 错误- 这表示与 Java 虚拟机操作相关的严重运行时错误。在大多数情况下,Error 不需要处理,因为它表明代码中存在一些严重的缺陷。其中最著名的是StackOverflowError(例如,当一个方法无休止地调用自身时会发生这种情况)和OutOfMemoryError(当没有足够的内存来创建新对象时会发生这种情况)。如您所见,在这些情况下,通常在运行时根本没有什么可处理的:代码只是写错了,需要重新编写。 异常- 这代表了一个异常:程序运行时发生的异常的、计划外的情况。它们不像错误那么严重,但它们仍然需要我们注意。所有异常都分为两种类型:checked和unchecked。 
Exception
。“检查”是什么意思?我们在上一课中提到了这一点: “Java 编译器因此知道最常见的异常以及它们可能发生的情况。” 例如,它知道如果代码从文件中读取数据,则该文件很可能不存在。并且有很多这样的情况(它可以推断)。因此,编译器会提前检查我们的代码是否存在这些潜在的异常。如果它找到它们,它不会编译代码,直到我们处理它们或重新抛出它们。第二种异常是“未检查”。它们派生自RuntimeException
类。它们与检查异常有何不同?似乎也有很多不同的类派生自RuntimeException
(描述运行时异常)。不同之处在于编译器不会预料到这些错误。好像在说,“写代码的时候,我没有发现任何可疑的地方,但是运行的时候出了问题,显然是代码有错误!” 的确如此。未经检查的异常通常是程序员错误的结果。 而且编译器显然无法预见人们可能亲手造成的每一种可能的不良情况。:) 因此,它不会检查我们的代码中是否处理了此类异常。您已经遇到过几个未经检查的异常:
- 除以零时发生ArithmeticException
- 当您尝试访问数组外的位置时,会发生ArrayIndexOutOfBoundsException 。
try-catch
块来检查您是否不小心被零除了?任何时候你访问一个数组,你都必须写一个try-catch
块来检查你的索引是否越界。一切都将是意大利面条式代码,并且将完全不可读。放弃这个想法是有道理的。因此,未经检查的异常不需要在try-catch
块中处理或重新抛出(尽管这在技术上是可行的,就像 Error 一样)。
如何抛出自己的异常
当然,Java 的创造者不可能预见到程序中可能出现的每一个异常情况。世界上的程序太多了,而且种类繁多。但这没什么好担心的,因为如果需要,您可以创建自己的异常。这很容易做到。您所要做的就是创建自己的类。您应该确保其名称以“Exception”结尾。编译器不需要这个,但是阅读你的代码的其他程序员会立即明白它是一个异常类。另外,指明该类是从该类继承的Exception
(编译器确实需要这样做)。例如,假设我们有一个Dog
类。我们可以使用walk()
方法。但在此之前,我们需要检查我们的宠物是否佩戴了项圈、皮带和口套。如果缺少任何这些装备,我们将抛出我们自己的异常:DogIsNotReadyException。它的代码如下所示:
public class DogIsNotReadyException extends Exception {
public DogIsNotReadyException(String message) {
super(message);
}
}
要表明该类是一个异常,需要在类名后写“ extends Exception ”(意思是“该类派生自Exception类”)。在构造函数中,我们只需使用Exception
String消息调用类构造函数(如果发生异常,我们将向用户显示消息,即错误描述)。这是我们的类代码中的样子:
public class Dog {
String name;
boolean isCollarPutOn;
boolean isLeashPutOn;
boolean isMuzzlePutOn;
public Dog(String name) {
this.name = name;
}
public static void main(String[] args) {
}
public void putCollar() {
System.out.println("The collar is on!");
this.isCollarPutOn = true;
}
public void putLeash() {
System.out.println("The leash is on!");
this.isLeashPutOn = true;
}
public void putMuzzle() {
System.out.println("The muzzle is on!");
this.isMuzzlePutOn = true;
}
public void walk() throws DogIsNotReadyException {
System.out.println("We're getting ready for a walk!");
if (isCollarPutOn && isLeashPutOn && isMuzzlePutOn) {
System.out.println("Hooray, let's go for a walk! " + name + " is very happy!");
} else {
throw new DogIsNotReadyException(name + " is not ready for a walk! Check the gear!");
}
}
}
现在我们的walk()
方法抛出DogIsNotReadyException。 这是通过关键字 throw 完成的。正如我们前面所说,异常是一个对象。因此,当我们的方法中发生异常(狗丢了东西)时,我们创建一个新DogIsNotReadyException
对象并使用关键字 throw 将其抛出。我们将“ throws DogIsNotReadyException ”添加到方法声明中。换句话说,现在编译器知道调用该walk()
方法可能会变成异常情况。因此,如果我们在程序的某处调用此方法,则必须处理此异常。让我们尝试在main()
方法中这样做:
public static void main(String[] args) {
Dog dog = new Dog("Buddy");
dog.putCollar();
dog.putMuzzle();
dog.walk();// Unhandled exception: DogIsNotReadyException
}
这不会编译。异常未处理!我们将代码包装在一个try-catch
块中以处理异常:
public static void main(String[] args) {
Dog dog = new Dog("Buddy");
dog.putCollar();
dog.putMuzzle();
try {
dog.walk();
} catch (DogIsNotReadyException e) {
System.out.println(e.getMessage());
System.out.println("Checking the gear! Is the collar on? " + dog.isCollarPutOn + "\r\n Is the leash on? "
+ dog.isLeashPutOn + "\r\n Is the muzzle on? " + dog.isMuzzlePutOn);
}
}
现在让我们看看控制台输出: 项圈打开了!枪口亮了!我们准备去散步了!Buddy 还没准备好散步!检查齿轮!检查齿轮!衣领上了吗?真的 系上皮带了吗?false 枪口是否打开?true 看看控制台输出的信息量有多大!我们看到了计划中的每一步;我们可以看到错误发生的位置,也可以立即准确地看到我们的狗丢失了什么。:) 这就是您创建自己的异常的方式。如您所见,这并不复杂。即使 Java 的创造者没有费心在语言中包含针对装备不佳的狗的特殊例外,我们已经修复了他们的疏忽。:)
更多阅读: |
---|
GO TO FULL VERSION