一、异常的概念
Java语言中,异常是程序运行时出现的不正常或意外情况。
二、异常的分类
1. 异常的分类
答:
Java异常都继承自 Throwable,主要分两类:
- Error:系统严重错误,如
OutOfMemoryError(堆内存溢出错误),程序无法处理。 - Exception:程序可处理异常,又分为:
- 受检异常:编译时需处理,如
IOException(输入输出异常)。 - 非受检异常:编译时不强制处理,如
NullPointerException(空指针异常)。
- 受检异常:编译时需处理,如
2. 受检异常和非受检异常在使用上有什么区别?
答:
- 受检异常继承自
Exception。使用时:- 要么在方法内部使用
try-catch捕获处理, - 要么在方法声明处使用
throws关键字抛出,否则编译不通过。
- 要么在方法内部使用
- 非受检异常继承自
RuntimeException。使用时:- 不需要在方法声明中显式抛出,
- 也不强制要求在方法内部捕获,编译能正常通过。
3. 常见的Error、受检异常、非受检异常有哪些?
答:
常见 Error
StackOverflowError:栈溢出错误OutOfMemoryError:内存溢出错误NoClassDefFoundError:类定义未找到错误LinkageError:链接错误
常见受检异常
IOException:输入输出异常SQLException:数据库操作异常ClassNotFoundException:类未找到异常InterruptedException:线程中断异常
常见非受检异常
NullPointerException:空指针异常ArrayIndexOutOfBoundsException:数组越界异常ArithmeticException:算术异常NumberFormatException:数字格式异常IllegalArgumentException:非法参数异常IndexOutOfBoundsException:索引越界异常
4. 分别给出一个Error、受检异常、非受检异常的简单示例代码。
答:
(1) Error 示例
StackOverflowError 属于 Error 类型,通常在方法无限递归调用时产生,因为每次方法调用都会在栈上分配空间,无限递归会使栈空间耗尽。
// 测试类
public class StackOverflowErrorExample {
public static void recursiveMethod() {
// 无限递归调用自身
recursiveMethod();
}
public static void main(String[] args) {
try {
recursiveMethod();
} catch (StackOverflowError e) {
System.out.println("捕获到 StackOverflowError: " + e);
}
}
}测试结果:
捕获到 StackOverflowError: java.lang.StackOverflowError
解释:
在 recursiveMethod 方法中,它不断地调用自身,没有终止条件,最终会导致栈空间耗尽,抛出 StackOverflowError。在 main 方法里,使用 try-catch 块捕获该错误并输出错误信息。
(2) 受检异常示例
IOException 是典型的受检异常,在进行文件操作、网络操作等可能发生输入输出问题的场景中经常出现,使用时必须进行处理。
import java.io.FileReader;
import java.io.IOException;
public class IOExceptionExample {
public static void readFile() throws IOException {
// 尝试打开一个文件进行读取
FileReader reader = new FileReader("nonexistentfile.txt");
int data;
while ((data = reader.read()) != -1) {
System.out.print((char) data);
}
reader.close();
}
public static void main(String[] args) {
try {
readFile();
} catch (IOException e) {
System.out.println("捕获到 IOException: " + e.getMessage());
}
}
}测试结果:
捕获到 IOException: nonexistentfile.txt (系统找不到指定的文件。)
解释:
readFile 方法尝试打开一个不存在的文件 nonexistentfile.txt 进行读取,这会引发 IOException。
由于 IOException 是受检异常,readFile 方法使用 throws 关键字声明抛出该异常。
在 main 方法中,调用 readFile 方法并使用 try-catch 块捕获并处理该异常。
(3) 非受检异常示例
NullPointerException 属于非受检异常,通常在尝试对一个 null 对象进行操作时抛出。
public class NullPointerExceptionExample {
public static void main(String[] args) {
String str = null;
try {
// 对 null 对象调用方法,会抛出 NullPointerException
int length = str.length();
System.out.println("字符串长度: " + length);
} catch (NullPointerException e) {
System.out.println("捕获到 NullPointerException: " + e);
}
}
}测试结果:
捕获到 NullPointerException: java.lang.NullPointerException
解释:
在 main 方法中,定义了一个 null 的字符串对象 str,然后尝试调用它的 length() 方法,这会导致 NullPointerException 异常。使用 try-catch 块捕获并处理该异常。
三、try-catch-finally 语句
1. 简述 try-catch-finally 语句块中各部分的作用
答:
try块包含可能抛出异常的代码;catch块用于捕获并处理try块中抛出的指定类型异常;finally块无论是否发生异常都会执行,常用于释放资源。
2. 在 try-catch-finally 结构中,如果 try 块中有 return 语句,finally 块中的代码会执行吗?
答:
会执行。
在 try 块遇到 return 语句时,会先保存返回值,然后执行 finally 块中的代码,最后再返回之前保存的值。
3. catch 块捕获异常后,若 finally 块中有 return 语句,会对 try 或 catch 块中的 return 结果有什么影响?
答:
若 finally 块中有 return 语句,会覆盖 try 或 catch 块中的 return 结果,最终返回 finally 块中 return 的值。
4. 请给出一个经典且简洁的try-catch-finally 语句的使用场景
答:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class TryCatchFinallyExample {
public static void main(String[] args) {
BufferedReader reader = null;
try {
// 尝试打开文件进行读取
reader = new BufferedReader(new FileReader("test.txt"));
String line;
// 逐行读取文件内容并输出
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
// 捕获并处理可能出现的输入输出异常
System.err.println("读取文件时出现错误: " + e.getMessage());
} finally {
try {
if (reader != null) {
// 确保文件读取器被关闭
reader.close();
}
} catch (IOException e) {
// 处理关闭文件读取器时可能出现的异常
System.err.println("关闭文件读取器时出现错误: " + e.getMessage());
}
}
}
}代码解释
try块: 代码尝试打开并读取文件test.txt的内容,将文件内容逐行输出。由于文件操作可能会出现异常,如文件不存在、文件无法访问等,所以这些操作被放在try块中。catch块: 当try块中的代码抛出IOException时,catch块会捕获该异常,并将错误信息打印到标准错误输出。finally块: 无论try块中是否发生异常,finally块中的代码都会执行。在这个例子中,finally块用于关闭文件读取器BufferedReader,以确保资源被正确释放。关闭操作也可能会抛出IOException,因此同样使用try-catch语句来处理。
四、自定义异常
1. 为什么需要自定义异常?
答:
在实际开发中,Java内置的异常类型可能无法精准描述业务场景中出现的问题。
自定义异常能使异常信息更具针对性,便于代码的维护和调试,让开发者可以根据业务需求对特定情况进行异常处理。
2. 简述自定义异常的步骤。
答:
(1) 创建一个类继承自 Exception(受检异常)或 RuntimeException(非受检异常)。
(2) 提供至少一个构造方法,通常包含一个接收异常信息字符串的构造方法。
(3) 若有需要,可添加其他自定义方法。
3. 给出一个经典的自定义异常的简单示例代码
答:
以下是一个自定义异常示例,模拟在一个简单的银行账户系统中,当尝试取款金额超过账户余额时抛出异常。(日常我们判定余额不足并不会使用异常抛出,这里只是作示例。)
自定义异常类
// 自定义异常类(余额不足异常),继承自 Exception,属于受检异常
class InsufficientBalanceException extends Exception {
// 构造方法,接收异常信息
public InsufficientBalanceException(String message) {
super(message);
}
}银行账户类
// 银行账户类
class BankAccount {
// 成员变量:账户余额
private double balance;
// 构造方法,初始化账户余额
public BankAccount(double initialBalance) {
this.balance = initialBalance;
}
// 取款方法,可能抛出 InsufficientBalanceException 异常
public void withdraw(double amount) throws InsufficientBalanceException {
if (amount > balance) {
// 当取款金额大于账户余额时,抛出异常
throw new InsufficientBalanceException("余额不足,无法取出 " + amount + " 元。");
}
balance -= amount;
System.out.println("成功取出 " + amount + " 元,当前余额为 " + balance + " 元。");
}
}测试代码
// 测试类
public class CustomExceptionTest {
public static void main(String[] args) {
// 创建一个初始余额为 1000 元的银行账户
BankAccount account = new BankAccount(1000);
try {
// 尝试取款 1500 元
account.withdraw(1500);
} catch (InsufficientBalanceException e) {
// 捕获并处理异常,输出异常信息
System.out.println("捕获到异常: " + e.getMessage());
}
try {
// 尝试取款 500 元
account.withdraw(500);
} catch (InsufficientBalanceException e) {
System.out.println("捕获到异常: " + e.getMessage());
}
}
}运行结果:
捕获到异常: 余额不足,无法取出 1500.0 元。
成功取出 500.0 元,当前余额为 500.0 元。
代码解释
- 自定义异常类
InsufficientBalanceException:继承自Exception,表示这是一个受检异常。它包含一个构造方法,用于接收异常信息并传递给父类。 - 银行账户类
BankAccount:包含一个balance字段表示账户余额,以及一个withdraw方法用于取款。在withdraw方法中,如果取款金额大于账户余额,就会抛出InsufficientBalanceException异常。 - 测试类
CustomExceptionTest:创建一个银行账户对象,分别尝试取款 1500 元和 500 元,并使用try-catch块捕获和处理可能抛出的InsufficientBalanceException异常。
