Java-Chapter 12 Exception Handling and Text IO

问题 1:什么是异常处理(Exception Handling)?

答案
异常处理是一种机制,使得方法可以将异常抛给调用者,从而让程序能以一种有序的方式应对错误和异常情况。

解释
异常处理可以帮助程序在运行时捕获错误,避免程序崩溃。它通过 trycatchfinally 块来捕捉和处理异常。异常可以由程序内部的错误或外部因素引起,比如文件未找到、网络连接中断等。

示例

1
2
3
4
5
6
7
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("Caught exception: " + e.getMessage());
} finally {
System.out.println("Finally block executed.");
}

问题 2:Java 中的异常是什么?

答案
Java 中的异常是 java.lang.Throwable 类的实例,所有异常类都是从 Throwable 类派生而来。异常分为两种类型:已检查异常(checked exceptions)和未检查异常(unchecked exceptions)。

解释

  • 已检查异常(Checked Exceptions):这些异常必须在方法中显式声明,或者捕获它们,否则编译会报错。常见的已检查异常有 IOExceptionClassNotFoundException
  • 未检查异常(Unchecked Exceptions):这些异常是运行时异常,不强制要求进行处理。常见的未检查异常有 NullPointerExceptionArithmeticException

示例

1
2
3
4
5
try {
throw new IOException("File not found");
} catch (IOException e) {
System.out.println("Caught IOException");
}

问题 3:如何声明异常?

答案
可以通过 throws 关键字在方法声明中声明异常,告诉编译器该方法可能会抛出哪种类型的异常。

解释
throws 关键字用于方法声明中,表示方法可能抛出的异常。调用该方法时,调用者必须处理这些异常,或者进一步声明这些异常。

示例

1
2
3
4
public void readFile(String filename) throws IOException {
// 可能会抛出IOException
FileReader file = new FileReader(filename);
}

问题 4:如何抛出异常?

答案
使用 throw 关键字可以在方法中抛出异常。

解释
throw 关键字用于抛出一个异常实例。抛出的异常可以是 Java 提供的预定义异常,也可以是自定义异常类的实例。

示例

1
2
3
4
5
public void checkAge(int age) {
if (age < 18) {
throw new IllegalArgumentException("Age must be 18 or older");
}
}

问题 5:什么是已检查异常和未检查异常?

答案

  • 已检查异常(Checked Exceptions):这些异常是在编译时被检查的,必须在代码中显式捕获或声明。常见的已检查异常包括 IOExceptionSQLException 等。
  • 未检查异常(Unchecked Exceptions):这些异常在运行时发生,不需要在方法中声明或捕获。常见的未检查异常有 NullPointerExceptionArithmeticException 等。

解释

  • 已检查异常:编译器要求程序员在代码中显式处理或声明这些异常。例如,IOException 需要被 try-catch 语句块捕获,或通过 throws 关键字声明抛出。
  • 未检查异常:这些异常不强制要求处理,因为它们通常代表程序中的逻辑错误,比如除以零时抛出的 ArithmeticException

示例

1
2
3
4
5
6
7
8
9
// 已检查异常(需要捕获或声明)
public void readFile() throws IOException {
throw new IOException("File not found");
}

// 未检查异常(可以不捕获或声明)
public void divide(int a, int b) {
int result = a / b; // 可能抛出 ArithmeticException
}
  1. 异常处理 是一种用于捕获和处理错误的机制,确保程序能够平稳运行。
  2. 异常类 都是 Throwable 的实例,分为已检查异常和未检查异常。
  3. 使用 throws 声明方法可能抛出的异常,使用 throw 抛出异常。
  4. 已检查异常 需要显式处理,而 未检查异常 不强制要求处理。

问题 6:如何处理已检查异常(Checked Exceptions)?

答案
为了调用声明了已检查异常的方法,必须将该方法调用放在 try 语句块中,并通过 catch 块捕获并处理异常。

解释
已检查异常是在编译时检查的,Java要求开发者在方法中明确处理这些异常。调用这些方法时,必须在代码中使用 try-catch 语句,捕获并处理这些异常,或者将异常声明抛给调用者。

示例

1
2
3
4
5
6
7
8
9
10
11
public void readFile(String filename) throws IOException {
FileReader file = new FileReader(filename);
}

public void processFile() {
try {
readFile("example.txt");
} catch (IOException e) {
System.out.println("Caught IOException: " + e.getMessage());
}
}

问题 7:如果异常在当前方法中没有被捕获,如何处理?

答案
如果异常没有在当前方法中被捕获,它将被传递给调用它的方法。这个过程会一直继续,直到异常被捕获,或者最终传递到 main 方法。

解释
异常处理遵循“传播”机制。当异常在一个方法中抛出并没有被捕获时,它会向上传递到调用该方法的代码,直到它被捕获或者没有地方捕获,程序就会终止。

示例

1
2
3
4
5
6
7
8
9
10
11
public void methodA() throws IOException {
throw new IOException("An error occurred");
}

public void methodB() {
try {
methodA(); // 这个方法调用methodA()
} catch (IOException e) {
System.out.println("Caught in methodB: " + e.getMessage());
}
}

问题 8:如何处理继承体系中的异常?

答案
可以通过捕获异常类的父类来捕获该父类所有子类的异常对象。

解释
Java 中,异常类是有继承关系的。通过捕获异常的父类,可以捕获所有继承自该父类的子类异常。例如,如果你捕获 Exception,那么所有从 Exception 继承的异常(如 IOExceptionNullPointerException 等)都会被捕获。

示例

1
2
3
4
5
try {
throw new IOException("IO error occurred");
} catch (Exception e) {
System.out.println("Caught Exception: " + e.getMessage()); // 也会捕获IOException
}

问题 9:在 catch 块中,异常的处理顺序为什么重要?

答案
catch 块中,捕获异常的顺序很重要。如果父类异常被列在子类异常之前,编译器会报错。

解释
Java 中的异常类有继承关系,如果你先捕获了父类异常,子类异常将永远无法被捕获,因为父类异常已经处理了所有的异常情况。因此,捕获子类异常的代码必须在捕获父类异常之前。

示例

1
2
3
4
5
6
7
8
// 错误示例:父类异常捕获在子类异常之前,会导致编译错误
try {
throw new IOException("File not found");
} catch (Exception e) { // 先捕获父类Exception
System.out.println("Exception: " + e.getMessage());
} catch (IOException e) { // 后捕获子类IOException
System.out.println("IOException: " + e.getMessage());
}

正确示例

1
2
3
4
5
6
7
8
// 正确示例:先捕获子类异常,再捕获父类异常
try {
throw new IOException("File not found");
} catch (IOException e) { // 先捕获子类IOException
System.out.println("IOException: " + e.getMessage());
} catch (Exception e) { // 后捕获父类Exception
System.out.println("Exception: " + e.getMessage());
}
  1. 已检查异常的处理:使用 try-catch 语句处理已检查异常,或者通过 throws 声明将其抛给调用者。
  2. 异常的传播:如果异常没有被当前方法捕获,它会向上传递到调用方法,直到被捕获或者终止程序。
  3. 继承体系中的异常:通过捕获父类异常来捕获所有子类异常。
  4. catch 块中的顺序:异常的捕获顺序非常重要,子类异常应先于父类异常捕获。

问题 10:finally 块中的代码何时执行?

答案
finally 块中的代码无论是否发生异常都会执行。

解释
finally 块是异常处理的一部分,它会在 try 块和 catch 块之后执行。即使在 try 块中发生异常并且异常被捕获,finally 块中的代码也会执行。如果没有异常发生,finally 块同样会被执行。它通常用于资源清理,比如关闭文件或数据库连接。

示例

1
2
3
4
5
6
7
8
try {
System.out.println("In try block");
int result = 10 / 0; // 发生异常
} catch (ArithmeticException e) {
System.out.println("Caught exception: " + e.getMessage());
} finally {
System.out.println("Finally block executed");
}

输出

1
2
3
In try block
Caught exception: / by zero
Finally block executed

问题11:异常处理的作用是什么?

答案
异常处理将错误处理代码与正常的程序任务分离,使得程序更加易于阅读和修改。

解释
通过异常处理,错误可以在独立的 catch 块中处理,而不需要与程序的主要业务逻辑混在一起。这种分离有助于使程序的控制流更清晰,并且在程序发生错误时,可以集中处理错误而不影响正常代码的运行。


问题 12:什么时候不应该使用异常处理?

答案
异常处理不应替代简单的条件判断。对于能够通过 if 语句检测和处理的简单错误,应使用条件判断,而异常处理应仅用于处理无法通过 if 语句处理的复杂错误。

解释
异常处理是用于捕获程序中意外的错误情况,对于那些可以通过常规条件判断避免或检测的问题,使用 if 语句会更加高效且可读。如果频繁使用异常处理来检查普通条件,反而会让程序复杂且不易理解。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 正确示例:简单的条件判断
if (number > 0) {
System.out.println("Positive number");
} else {
System.out.println("Non-positive number");
}

// 错误示例:不需要使用异常处理来捕获条件
try {
if (number > 0) {
System.out.println("Positive number");
} else {
throw new IllegalArgumentException("Non-positive number");
}
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}

问题 13:如何使用 File 类操作文件?

答案
File 类用于获取文件的属性和操作文件,但不能用于创建文件或读写数据。

解释
File 类提供了检查文件是否存在、删除文件、获取文件路径、大小等功能,但它不提供文件的读写操作。创建文件或读写数据通常使用其他类,如 FileOutputStreamFileInputStreamBufferedReaderPrintWriter 等。


问题 14:如何在 Java 中读写文件?

答案
可以使用 Scanner 类读取文本文件中的字符串和基本数据类型的值,使用 PrintWriter 创建文件并向文件写入数据。

解释
Scanner 是一个用于从文件读取数据的类,支持读取各种数据类型。PrintWriter 类则用于将数据写入文件。通过使用 try-with-resources 语句,可以确保文件资源在使用完后自动关闭。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 使用 Scanner 读取文件
try (Scanner scanner = new Scanner(new File("input.txt"))) {
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException e) {
System.out.println("File not found");
}

// 使用 PrintWriter 写入文件
try (PrintWriter writer = new PrintWriter(new File("output.txt"))) {
writer.println("Hello, World!");
} catch (FileNotFoundException e) {
System.out.println("Error writing to file");
}

问题 15:什么是 try-with-resources 语句?

答案
try-with-resources 是一个用于自动关闭资源的语句。在 try 语句中声明的资源会在 try 块结束时自动关闭,无需显式调用 close() 方法。

解释
try-with-resources 自动管理资源的关闭,确保无论 try 块中的代码是否抛出异常,资源都会在 try 块执行完毕后自动关闭。这对于文件、数据库连接、网络连接等资源的管理非常重要。

示例

1
2
3
4
5
6
7
8
// 使用 try-with-resources 自动关闭资源
try (Scanner scanner = new Scanner(new File("input.txt"))) {
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException e) {
System.out.println("File not found");
}
  1. finally 块中的代码总是执行,无论是否发生异常。
  2. 异常处理有助于将错误处理代码与正常任务分开,使得代码更清晰。
  3. 异常处理不应替代简单的条件判断,应仅用于无法通过常规判断解决的复杂问题。
  4. File 类用于获取文件信息和执行文件操作,但不进行读写操作。
  5. 使用 Scanner 读取文件数据,使用 PrintWriter 写入文件数据,结合 try-with-resources 可以自动关闭资源。