你好!我不想提它,但程序员工作的很大一部分是处理错误。大多数情况下,他或她自己的。原来没有不犯错的人。而且也没有这样的程序。
当然,在处理错误时,最主要的是了解它的原因. 很多事情都可能导致程序中出现错误。在某些时候,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,则会发生此异常。
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
,catch
和finally
. 
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();
}
}
}
现在我们确信我们会处理资源,不管程序运行时发生了什么。:) 关于异常,您需要了解的还不止这些。错误处理是编程中一个非常重要的话题。很多文章都专门介绍它。在下一课中,我们将了解有哪些类型的异常以及如何创建您自己的异常。:) 回头见!
更多阅读: |
---|
GO TO FULL VERSION