CodeGym /Java 博客 /随机的 /异常:捕获和处理
John Squirrels
第 41 级
San Francisco

异常:捕获和处理

已在 随机的 群组中发布
你好!我不想提它,但程序员工作的很大一部分是处理错误。大多数情况下,他或她自己的。原来没有不犯错的人。而且也没有这样的程序。 例外:捕捉和处理 - 1 当然,在处理错误时,最主要的是了解它的原因. 很多事情都可能导致程序中出现错误。在某些时候,Java 的创建者问自己应该如何处理最有可能的编程错误?完全避免它们是不现实的,程序员有能力写出你甚至无法想象的东西。:) 因此,我们需要为语言提供一种处理错误的机制。换句话说,如果您的程序出现错误,您需要某种脚本来指示下一步该做什么。当错误发生时,程序究竟应该做什么?今天我们将熟悉这种机制。它被称为“ Java 中的异常”。

什么是例外?

异常是程序运行时发生的异常的、计划外的情况。 有很多例外。例如,您编写了从文件中读取文本并显示第一行的代码。

public class Main {

   public static void main(String[] args) throws IOException {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
       String firstString = reader.readLine();
       System.out.println(firstString);
   }
}
但是如果没有这个文件怎么办!该程序将生成异常:FileNotFoundException。Output: Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (系统找不到指定的路径) 在Java中,每个异常都由一个单独的类表示。 所有这些异常类都派生自一个共同的“祖先”——Throwable父类。异常类的名称通常简明地反映异常发生的原因:
  • FileNotFoundException(找不到文件)

  • ArithmeticException(执行数学运算时发生异常)

  • ArrayIndexOutOfBoundsException(索引超出了数组的范围)。例如,如果您尝试显示只有 10 个元素的数组的位置 23,则会发生此异常。
Java 总共有将近400 个这样的类!为什么这么多?让程序员更方便地使用它们。想象一下:您编写了一个程序,当它运行时会生成如下所示的异常:

Exception in thread "main"
呃。:/ 这没什么用。目前尚不清楚错误的含义或来源。这里没有有用的信息。但是 Java 中的大量异常类为程序员提供了最重要的东西:错误的类型及其可能的原因(嵌入在类名中)。这是另一回事

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (The system cannot find the specified path)
立即清楚问题可能是什么以及从哪里开始挖掘以解决问题! 与任何类的实例一样,异常是对象。

捕获和处理异常

Java 有处理异常的特殊代码块:try,catchfinally. 例外:捕捉和处理 - 2 程序员认为可能发生异常的代码放在try块中。这并不意味着这里会发生异常。这意味着它可能会发生在这里,并且程序员意识到了这种可能性。您期望发生的错误类型被放置在catch块中。这还包含发生异常时应执行的所有代码。这是一个例子:

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {

       System.out.println("Error! File not found!");
   }
}
输出: 错误!文件未找到! 我们将代码分为两块。在第一个块中,我们预计可能会出现“找不到文件”错误。这是try块。第二步,我们告诉程序如果发生错误该怎么办。以及具体的错误类型:FileNotFoundException。如果我们将不同的异常类放在块的括号中catch,则FileNotFoundException不会被捕获。

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (ArithmeticException e) {

       System.out.println("Error! File not found!");
   }
}
Output: Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (The system cannot find the specified path)块 中的代码catch没有运行,因为我们“配置”这个块要捕获ArithmeticException,并且块中的代码try抛出了不同的类型:FileNotFoundException。我们没有编写任何代码来处理FileNotFoundException,因此程序显示 的默认信息FileNotFoundException。这里需要注意三点。 第一。一旦块中某行发生异常try,后面的代码将不会执行。程序的执行立即“跳转到”catch块。例如:

public static void main(String[] args) {
   try {
       System.out.println("Divide by zero");
       System.out.println(366/0);// This line of code will throw an exception

       System.out.println("This");
       System.out.println("code");
       System.out.println("will not");
       System.out.println("be");
       System.out.println("executed!");

   } catch (ArithmeticException e) {

       System.out.println("The program jumped to the catch block!");
       System.out.println("Error! You can't divide by zero!");
   }
}
输出: 除以零 程序跳转到 catch 块!错误!你不能除以零! 在该块的第二行try,我们尝试除以 0,结果是一个ArithmeticException. 因此,try块的第 3-9 行将不会执行。正如我们所说,程序立即开始执行catch块。 第二。可以有几个catch块。如果块中的代码try可能抛出的不是一个,而是几种不同类型的异常,您可以catch为它们中的每一个编写一个块。

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       System.out.println(366/0);
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
      
       System.out.println("Error! File not found!");
      
   } catch (ArithmeticException e) {

       System.out.println("Error! Division by 0!");
      
   }
}
在这个例子中,我们写了两个catch块。如果块FileNotFoundException中出现a,则将执行try第一个块。catch如果ArithmeticException发生 an,将执行第二个块。catch如果你愿意,你可以写 50 个块。当然,最好不要编写可能引发 50 种不同异常的代码。:) 第三。您如何知道您的代码可能抛出哪些异常? 好吧,您也许能够猜出其中的一些,但您不可能将所有内容都记在脑海中。 因此,Java 编译器知道最常见的异常以及它们可能发生的情况。 例如,如果您编写的代码编译器知道可能会抛出两种类型的异常,那么在您处理它们之前,您的代码将无法编译。我们将在下面看到这方面的例子。现在谈谈异常处理。有两种处理异常的方法。我们已经遇到了第一个:该方法可以在一个块中处理异常本身catch()。还有第二种选择:该方法可以将异常重新抛出调用堆栈。这意味着什么?例如,我们有一个具有相同printFirstString()方法的类,它读取一个文件并显示其第一行:

public static void printFirstString(String filePath) {

   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
目前,我们的代码无法编译,因为它有未处理的异常。在第 1 行中,您指定文件的路径。编译器知道这样的代码很容易产生一个FileNotFoundException. 在第 3 行,您从文件中读取了文本。此过程很容易导致IOException(输入/输出错误)。现在编译器对你说,“伙计,我不会批准这段代码,我不会编译它,直到你告诉我如果发生这些异常之一我应该怎么做。而且它们肯定会根据你编写的代码发生!” 没有办法解决它:您需要同时处理这两个问题!我们已经知道第一种异常处理方法:我们需要将我们的代码放在一个try块中,并添加两个catch块:

public static void printFirstString(String filePath) {

   try {
       BufferedReader reader = new BufferedReader(new FileReader(filePath));
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       System.out.println("Error, file not found!");
       e.printStackTrace();
   } catch (IOException e) {
       System.out.println("File input/output error!");
       e.printStackTrace();
   }
}
但这不是唯一的选择。我们可以简单地抛出更高的异常,而不是在方法中编写错误处理代码。throws这是使用方法声明中的 关键字完成的:

public static void printFirstString(String filePath) throws FileNotFoundException, IOException {
   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
在关键字之后throws,我们指明了该方法可能抛出的所有异常类型的逗号分隔列表。为什么?现在,如果有人想调用printFirstString()程序中的方法,他或她(而不是你)将不得不执行异常处理。例如,假设您的一位同事在程序的其他地方编写了一个调用您的printFirstString()方法的方法:

public static void yourColleagueMethod() {

   // Your colleague's method does something

   //...and then calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Henry\\Desktop\\testFile.txt");
}
我们得到一个错误!这段代码无法编译!我们没有在printFirstString()方法中编写异常处理代码。因此,这项任务现在落在了使用该方法的人的肩上。 换句话说,该methodWrittenByYourColleague()方法现在有两个相同的选项:它必须要么使用一个try-catch块来处理这两个异常,要么重新抛出它们。

public static void yourColleagueMethod() throws FileNotFoundException, IOException {
   // The method does something

   //...and then calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Henry\\Desktop\\testFile.txt");
}
在第二种情况下,调用堆栈中的下一个方法(调用的方法methodWrittenByYourColleague())将必须处理异常。这就是为什么我们称之为“向上抛出或传递异常”的原因。如果您使用关键字向上抛出异常throws,您的代码将通过编译。此时,编译器似乎在说,“好吧,好吧。你的代码包含一堆潜在的异常,但我会编译它。但我们会回到这个话题!” 当您调用任何具有未处理异常的方法时,编译器会履行其承诺并再次提醒您有关它们的信息。最后,我们将讨论finally块(抱歉是双关语)。这是异常处理三巨头的最后一部分try-catch-finally.

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       System.out.println("Error! File not found!");
       e.printStackTrace();
   } finally {
       System.out.println ("And here's the finally block!");
   }
}
在此示例中,块内的代码finally将在两种情况下执行。 如果块中的代码try完全运行且没有抛出任何异常,则该finally块将运行到最后。如果块内的代码try被异常中断,程序跳转到catch块内,块finally内的代码仍然会在块内的代码之后运行catch为什么这是必要的? 它的主要目的是执行强制代码:无论在什么情况下都必须执行的代码。例如,它经常释放一些程序使用的资源。在我们的代码中,我们打开一个流以从文件中读取信息并将其传递给BufferedReader对象。 我们必须关闭阅读器并释放资源。无论如何都必须这样做——当程序正常工作时,以及当它抛出异常时。该finally块是执行此操作的一个非常方便的地方:

public static void main(String[] args) throws IOException {

   BufferedReader reader = null;
   try {
       reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       e.printStackTrace();
   } finally {
       System.out.println ("And here's the finally block!");
       if (reader != null) {
           reader.close();
       }
   }
}
现在我们确信我们会处理资源,不管程序运行时发生了什么。:) 关于异常,您需要了解的还不止这些。错误处理是编程中一个非常重要的话题。很多文章都专门介绍它。在下一课中,我们将了解有哪些类型的异常以及如何创建您自己的异常。:) 回头见!
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION