一、什么是异常
- 定义
- 在Java中,异常是在程序执行期间发生的事件,它中断了指令的正常流程。异常可以是由程序本身产生的错误,例如除以零的操作,也可以是由外部因素引起的,比如试图读取一个不存在的文件。
- 异常类层次结构
- 在Java中,所有的异常类都继承自
java.lang.Throwable
类。Throwable
类有两个重要的子类:Exception
和Error
。 Exception
类:表示程序本身可以处理的异常情况。例如FileNotFoundException
(当试图打开一个不存在的文件时抛出)、ArithmeticException
(进行非法的算术运算,如除以零)等。Error
类:表示严重的错误,通常是由Java虚拟机(JVM)内部错误或者资源耗尽等严重问题导致的,程序通常无法处理这些错误。例如OutOfMemoryError
。
- 在Java中,所有的异常类都继承自
一、常见的Exception类及对应的异常情况
- ArithmeticException(算术异常)
- 异常情况:发生在进行非法的算术运算时,如除以零。
- 示例用法:
try {int num1 = 10;int num2 = 0;int result = num1/num2;
} catch (ArithmeticException e) {System.out.println("算术运算错误:" + e.getMessage());
}
- ArrayIndexOutOfBoundsException(数组下标越界异常)
- 异常情况:当访问数组元素时使用了超出数组范围的下标。
- 示例用法:
try {int[] arr = {1, 2, 3};int element = arr[3];
} catch (ArrayIndexOutOfBoundsException e) {System.out.println("数组下标越界:" + e.getMessage());
}
- NullPointerException(空指针异常)
- 异常情况:当试图访问一个空对象的成员(如方法或变量)时发生。
- 示例用法:
try {String str = null;int length = str.length();
} catch (NullPointerException e) {System.out.println("空指针异常:" + e.getMessage());
}
- FileNotFoundException(文件未找到异常)
- 异常情况:当试图打开一个不存在的文件时抛出。
- 示例用法(假设读取文件操作):
import java.io.File;
import java.io.FileReader;
import java.io.IOException;try {File file = new File("nonexistent.txt");FileReader reader = new FileReader(file);
} catch (FileNotFoundException e) {System.out.println("文件未找到:" + e.getMessage());
} catch (IOException e) {// 捕获其他可能的I/O异常System.out.println("I/O异常:" + e.getMessage());
}
- NumberFormatException(数字格式异常)
- 异常情况:当试图将一个不符合数字格式的字符串转换为数字类型(如
Integer.parseInt
或Double.parseDouble
)时发生。 - 示例用法:
- 异常情况:当试图将一个不符合数字格式的字符串转换为数字类型(如
try {String numStr = "abc";int num = Integer.parseInt(numStr);
} catch (NumberFormatException e) {System.out.println("数字格式异常:" + e.getMessage());
}
二、如何确定要写什么异常处理
- 分析代码逻辑
- 查看可能出现问题的代码操作。例如,如果有除法运算,就可能出现
ArithmeticException
;如果有数组访问,就可能出现ArrayIndexOutOfBoundsException
。
- 查看可能出现问题的代码操作。例如,如果有除法运算,就可能出现
- 考虑对象的状态
- 当使用对象时,如果对象可能为
null
,就要考虑NullPointerException
。比如在使用从其他地方获取的对象引用,且没有进行null
检查的情况下。
- 当使用对象时,如果对象可能为
- 遵循Java API的文档说明
- 当使用Java类库中的方法时,查看方法的文档,了解可能抛出的异常类型。例如,
FileReader
的构造函数可能抛出FileNotFoundException
,根据文档就可以在代码中进行相应的异常处理。
- 当使用Java类库中的方法时,查看方法的文档,了解可能抛出的异常类型。例如,
二、异常处理机制
- try - catch块
- try块:包含可能会抛出异常的代码。例如:
try {int result = 10 / 0;
} catch (ArithmeticException e) {// 这里处理ArithmeticException异常System.out.println("除数不能为零:" + e.getMessage());
}
- catch块:用于捕获并处理try块中抛出的异常。可以有多个catch块来捕获不同类型的异常。catch块中的参数是一个异常对象,通过这个对象可以获取异常的相关信息,如异常的消息等。
- finally块
- finally块是可选的,但是如果存在,则不管try块中是否抛出异常,finally块中的代码都会被执行。例如:
try {// 可能抛出异常的代码
} catch (Exception e) {// 处理异常
} finally {// 这里的代码总是会执行,例如关闭资源(文件流、数据库连接等)
}
异常处理机制(try - catch - finally)的进一步解释
- try - catch块的本质是处理异常情况
- 当我们编写代码时,有些操作可能会出错,例如访问数组元素时可能超出数组范围。在
try
块中,我们放置这些可能会出错的代码。当错误(异常)发生时,程序不会直接崩溃,而是会跳转到对应的catch
块中。 - 例如,假设我们有一个数组
int[] arr = {1, 2, 3};
,如果我们不小心写成int num = arr[5];
(超出了数组的索引范围),这就会产生ArrayIndexOutOfBoundsException
。如果没有try - catch
块,程序会因为这个错误而终止运行。但如果我们把这段代码放在try
块中,像这样:
- 当我们编写代码时,有些操作可能会出错,例如访问数组元素时可能超出数组范围。在
try {int[] arr = {1, 2, 3};int num = arr[5];
} catch (ArrayIndexOutOfBoundsException e) {System.out.println("数组索引超出范围,请检查代码:" + e.getMessage());
}
- 程序就会捕获这个异常,执行
catch
块中的代码,然后继续执行catch
块之后的其他代码,而不是直接崩溃。
- finally块是资源管理的保障
- 有时候我们在
try
块中会打开一些资源,比如文件流或者数据库连接。无论try
块中的操作是否成功(也就是无论是否抛出异常),我们都希望能够关闭这些资源,以避免资源泄漏(例如文件被占用无法删除,数据库连接过多等问题)。 - 例如,当我们打开一个文件进行读取操作时:
- 有时候我们在
FileReader reader = null;
try {File file = new File("test.txt");reader = new FileReader(file);// 读取文件内容的操作
} catch (FileNotFoundException e) {System.out.println("文件未找到:" + e.getMessage());
} finally {if (reader!= null) {try {reader.close();} catch (IOException e) {System.out.println("关闭文件时出错:" + e.getMessage());}}
}
- 在这个例子中,即使在
try
块中打开文件时出现FileNotFoundException
,finally
块中的代码也会尝试关闭文件流(如果已经成功打开的话)。
三、抛出异常
- throw关键字
- 可以使用
throw
关键字在方法内部主动抛出一个异常。例如:
- 可以使用
public void divide(int a, int b) {if (b == 0) {throw new ArithmeticException("除数不能为零");}int result = a / b;
}
- 声明异常(throws关键字)
- 如果一个方法可能会抛出某种异常,但是不想在方法内部处理这个异常,可以使用
throws
关键字在方法签名中声明这个异常。例如:
- 如果一个方法可能会抛出某种异常,但是不想在方法内部处理这个异常,可以使用
public void readFile() throws FileNotFoundException {// 代码可能会抛出FileNotFoundException
}
通过合理地使用Java异常处理机制,可以提高程序的健壮性和可靠性,使程序能够更好地应对各种可能出现的错误情况。
抛出异常(throw和throws)的进一步解释
- throw是主动报告错误
- 假设我们正在编写一个方法来计算两个数的除法,但是我们不希望除数为0。我们可以在方法内部使用
throw
来主动抛出一个异常,以表示这种错误情况。
- 假设我们正在编写一个方法来计算两个数的除法,但是我们不希望除数为0。我们可以在方法内部使用
public static int divide(int dividend, int divisor) {if (divisor == 0) {throw new ArithmeticException("除数不能为零");}return dividend / divisor;
}
- 这里,当
divisor
为0时,我们不是简单地让程序产生一个默认的错误(这可能会导致程序崩溃或者产生难以理解的结果),而是主动抛出一个明确的ArithmeticException
,并且附上了错误消息“除数不能为零”。
- throws是传递异常处理责任
- 考虑这样一种情况,我们有一个方法
methodA
调用了另一个方法methodB
,而methodB
可能会抛出异常。如果methodA
本身不知道如何处理这个异常,它可以使用throws
关键字将异常处理的责任传递给调用methodA
的方法。
- 考虑这样一种情况,我们有一个方法
public static void methodB() throws FileNotFoundException {File file = new File("nonexistent.txt");FileReader reader = new FileReader(file);
}public static void methodA() throws FileNotFoundException {methodB();
}
- 在这个例子中,
methodB
可能会抛出FileNotFoundException
,methodA
调用methodB
,但是methodA
不想处理这个异常,所以它在自己的方法签名中使用throws
关键字声明可能会抛出FileNotFoundException
。这样,调用methodA
的方法就需要处理这个异常了。
四、异常类型声明
ArithmeticException
、NullPointerException
、FileNotFoundException
-
类型匹配原则
- 在
catch
块中的ArithmeticException e
、NullPointerException e
、FileNotFoundException e
等语句里,前面的部分(如ArithmeticException
、NullPointerException
、FileNotFoundException
)代表的是异常类型。 - 这意味着
catch
块将只捕获指定类型的异常。例如,catch (ArithmeticException e)
只会捕获在try
块中抛出的ArithmeticException
类型的异常。如果try
块中抛出了其他类型的异常(如NullPointerException
),这个catch
块将不会捕获它。
- 在
-
异常类层次结构的影响
- 在Java中,异常类有层次结构。如果一个
catch
块声明的异常类型是某个异常类的父类,那么它可以捕获该父类及其所有子类类型的异常。 - 例如,
Exception
是ArithmeticException
、NullPointerException
、FileNotFoundException
等许多异常类的父类。如果有catch (Exception e)
这样的语句,它可以捕获所有这些类型的异常。但是,通常建议尽量使用具体的异常类型进行捕获,这样可以更有针对性地处理不同类型的异常。
- 在Java中,异常类有层次结构。如果一个
五、异常类型名字后的变量 如ArithmeticException e中的e
-
变量命名规则
- 在
catch
块中,(ArithmeticException e)
、(NullPointerException e)
或者(FileNotFoundException e)
里的e
只是一个变量名。这个变量名可以按照Java的变量命名规则进行修改。 - 例如,可以写成
(ArithmeticException ex)
或者(NullPointerException error)
等。
- 在
-
变量的意义
-
这个变量代表捕获到的异常对象。通过这个对象,我们可以获取关于异常的各种信息,例如异常的类型(通过
getClass()
方法)、异常的消息(通过getMessage()
方法)以及异常发生时的堆栈跟踪信息(通过printStackTrace()
方法)等。 -
例如:
-
try {// 可能抛出异常的代码
} catch (ArithmeticException myException) {System.out.println("捕获到的异常类型:" + myException.getClass().getName());System.out.println("异常消息:" + myException.getMessage());myException.printStackTrace();
}
- 在这个例子中,
myException
是我们自定义的变量名,它的功能和使用e
作为变量名是一样的,都是用来操作捕获到的异常对象。