你好!在上一讲中,我们熟悉了 Java 语言的异常方面,并看到了使用它们的示例。今天我们将更深入地了解它们的结构,并学习如何编写我们自己的异常:)
例外的类型
正如我们所说,Java中有很多异常类,几乎有400个!但它们都分为不同的组,所以很容易记住它们。它看起来像这样: 所有异常都有一个共同的祖先类Throwable
。两大类来自于它——异常(Exception)和错误(Error)。错误是与Java虚拟机操作相关的程序执行期间的严重错误。在大多数情况下,错误不需要处理,因为它表明代码中存在一些严重缺陷。最著名的错误:StackOverflowError
- 例如,当一个方法无休止地调用自身时发生;以及OutOfMemoryError
- 当没有足够的内存来创建新对象时发生。正如您所看到的,在这些情况下,大多数情况下没有什么特别需要处理的 - 代码只是写得不正确,需要重做。 异常实际上就是异常:程序运行时发生的异常的、计划外的情况。这些错误不像Error那么严重,但需要我们注意。所有异常分为2种类型——已检查(checked)和未检查(unchecked)。 所有已检查的异常都来自Exception
. “可验证”是什么意思?我们在上一讲中部分提到了这一点: “...Java 编译器知道最常见的异常,并且知道它们在什么情况下会发生。” 例如,他知道如果程序员用代码从文件中读取数据,很容易出现该文件不存在的情况。而且这样的情况还有很多,他是可以提前预测到的。因此,编译器会提前检查我们的代码是否存在潜在的异常。如果它找到它们,它不会编译代码,直到我们处理它们或将它们转发到顶部。第二种异常是“未经检查的”。他们来自班级RuntimeException
。它们与正在测试的有什么不同?似乎还有一堆不同的类来自RuntimeException
并描述特定的运行时异常。不同之处在于编译器不会预料到这些错误。他似乎在说:“在编写代码时,我没有发现任何可疑之处,但在运行时出了问题。显然代码中有错误!” 确实如此。未经检查的异常通常是程序员错误的结果。 并且编译器显然无法提供人们可以亲手创建的所有可能的错误情况:)因此,它不会检查我们代码中此类异常的处理。您已经遇到过几个未经检查的异常:
ArithmeticException
除以零时发生ArrayIndexOutOfBoundsException
尝试访问数组外部的单元格时发生。
如何抛出异常
当然,Java 的创建者无法应对程序中可能出现的所有异常情况。世界上的程序太多,而且差异太大。但这没关系,因为如果需要,您可以创建自己的异常。这很容易完成。您所要做的就是创建自己的类。其名称必须以“Exception”结尾。编译器不需要这个,但是阅读代码的程序员会立即明白这是一个异常类。另外,需要表明该类来自于该类Exception
。这对于编译器和正确操作来说已经是必要的。例如,我们有一个 Dog 类Dog
。我们可以使用 遛狗walk()
。但在此之前,我们需要检查我们的宠物是否戴着项圈、皮带和口套。如果缺少其中任何一个,我们将抛出我们自己的异常DogIsNotReadyException
。它的代码如下所示:
public class DogIsNotReadyException extends Exception {
public DogIsNotReadyException(String message) {
super(message);
}
}
为了表明一个类是异常,你需要在类名后面写extends Exception :这意味着“该类派生自Exception类”。在构造函数中,我们将简单地用一行调用类构造函数Exception
-message
它将向用户显示一条来自系统的消息,描述已发生的错误。这就是我们的类代码中的样子:
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("Let's go for a walk!");
if (isCollarPutOn && isLeashPutOn && isMuzzlePutOn) {
System.out.println("Hurrah, let's go for a walk!" + name + " I am glad!");
} else {
throw new DogIsNotReadyException("Dog " + name + "not ready for a walk! Check your gear!");
}
}
}
现在我们的方法walk()
抛出异常DogIsNotReadyException
。这是使用关键字 完成的throw
。正如我们之前所说,异常是一个对象。因此,在我们的方法中,当发生异常情况时(狗身上缺少某些东西),我们创建一个新的类对象DogIsNotReadyException
并使用单词将其放入程序中throw
。walk()
我们将 throws 添加到方法签名中DogIsNotReadyException
。换句话说,编译器现在知道方法调用walk()
可能会导致异常。因此,当我们在程序中的某个地方调用它时,就需要处理异常。让我们尝试在方法中执行此操作main()
:
public static void main(String[] args) {
Dog dog = new Dog("Mukhtar");
dog.putCollar();
dog.putMuzzle();
dog.walk();//Unhandled exception: DogIsNotReadyException
}
无法编译,异常未处理!让我们将代码包装在一个块中try-catch
来处理异常:
public static void main(String[] args) {
Dog dog = new Dog("Mukhtar");
dog.putCollar();
dog.putMuzzle();
try {
dog.walk();
} catch (DogIsNotReadyException e) {
System.out.println(e.getMessage());
System.out.println("Checking equipment! Is the collar on?" + dog.isCollarPutOn + "\r\n Is the leash on?"
+ dog.isLeashPutOn + "\r\n Are you wearing a muzzle?" + dog.isMuzzlePutOn);
}
}
现在让我们看看控制台输出:
Ошейник надет!
Намордник надет!
Собираемся на прогулку!
Собака Мухтар не готова к прогулке! Проверьте экипировку!
Проверяем снаряжение! Ошейник надет? true
Поводок надет? false
Намордник надет? true
看看控制台输出的信息变得多么丰富!我们可以看到程序中发生的每一步;我们看到错误发生的位置并立即注意到我们的狗到底缺少什么:)这就是我们创建自己的异常的方式。如您所见,没什么复杂的。尽管 Java 开发人员并没有费心在他们的语言中为装备不正确的狗添加一个特殊的例外,但我们纠正了他们的疏忽:)
GO TO FULL VERSION