原文链接
Java 异常处理
你将学到
-
Java中异常的捕获方法
-
throws/throw 和 finally 关键字
-
Java中常见的异常
-
Java中异常类层次结构
-
常见的异常错误的使用方式
-
自定义异常的方法
Java中异常的捕获方法
语法:
try {
// 可能发生异常的程序代码
}catch(ExceptionName e) {
//捕获到名为ExceptionName的异常的处理代码
}
<table><tr><td bgcolor=#D1EEEE> 解释:<font color="#dd0000"> try </font> 部分包裹着可能发生异常的代码,可以保护程序发生异常时不突然结束,而是被<font color="#dd0000"> catch </font>部分捕获到异常,从而让程序员有机会处理这个异常,保证程序继续走下去。</td></tr></table>
举例:
public class Main {
public static void main(String[] args) {
try {
int[] data = new int[]{1,2,3};
getDataByIndex(-1,data);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
public static int getDataByIndex(int index,int[] data) {
if(index<0||index>=data.length)
throw new ArrayIndexOutOfBoundsException("数组下标越界");
return data[index];
}
}
在上面的例子中,当数组越界的时候方法getDataByIndex会抛出异常,因为我们在main方法中用try...catch包裹了这个方法的调用,所以当异常发生时,main方法中的catch就会捕获到这个异常,这里的处理方式是将异常打印到控制台(开发中debug阶段经常用这种方式来调试代码,但是,realse阶段需要将异常写入日志)。
throws/throw和 finally 关键字
<font color="#000000">throw 关键字</font><br />
上例中我们已经在getDataByIndex方法中用到了throw
关键字来抛出一个异常。
在写代码时,如果某些情况下不允许程序继续走下去,就可以像上面一样,抛出一个异常给调用者,以便调用者知道发生了异常,并对异常情况进行处理。
<font color="#000000">throws 关键字</font><br />
上面的例子是主动抛出异常(throw表示直接抛出异常),但是某些情况下我们希望表达某个方法有可能发生异常,可以用throws
关键字。
举例:
public void withdraw(double amount) throws RemoteException, InsufficientFundsException
{
// Method implementation
}
表示withdraw方法可能抛出RemoteException或者InsufficientFundsException两种异常。如果调用这样一个方法,Java编译机会要求调用者添加try...catch的代码来捕获异常,而这里有可能发生两种异常,我们可以使用多重捕获块
:
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}
<font color="#000000">finally 关键字</font><br />
无论是否发生异常,都希望执行的代码,可以放在finally
关键子块里:
语法:
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}finally{
// 无论是否发生异常,都会执行的程序代码
}
举例:
public class ExcepTest{
public static void main(String args[]){
int a[] = new int[2];
try{
System.out.println("Access element three :" + a[3]);
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("Exception thrown :" + e);
}
finally{
a[0] = 6;
System.out.println("First element value: " +a[0]);
System.out.println("The finally statement is executed");
}
}
}
结果:
Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3
First element value: 6
The finally statement is executed
Java中常见的异常
<table><tr><td bgcolor=#D8D8D8>运行时异常</td></tr></table>
异常名 | 异常解析 |
---|---|
ArrayIndexOutOfBoundException | 数组索引越界异常 |
ArithmeticException | 算术条件异常,如除数为0 |
NullPointException | 空指针异常 |
ClassNotFoundException | 找不到类异常 |
NegativeArraySizeException | 数组长度为负异常 |
ArrayStoreException | 数组中包含不兼容的值抛出异常 |
SecurityException | 安全性异常 |
IllegalArgumentException | 非法参数异常 |
<table><tr><td bgcolor=#D8D8D8>IOException</td></tr></table>
异常名 | 异常解析 |
---|---|
IOException | 操作输入流和输出流可能出现的异常 |
EOFException | 文件已结束异常 |
FileNotFoundException | 文件未找到异常 |
常见的异常绝非仅此而已,具体的只能使用的时候碰到再具体分析。
Java中异常类层次结构
系统的异常类层次分明

虽然结构看着很多,但是需要程序员处理的一般只有Exception类型的异常。因为Error通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程。
- 运行时异常(非检查性异常):包括RuntimeException及其子类和Error。
- 检查性异常:在编译时不能被简单地忽略,例如用户错误或问题引起的异常(如打开一个不存在文件时)。
异常错误的使用方式
<font color="#dd0000">选择异常</font><br />
1、是否需要立即终止。
2、是否需要进一步处理和恢复。
举例:
将 SQLException 定义为非检测异常(就是不需要立即停止的异常),这样操作数据时开发人员理所当然的认为 SQLException 不需要调用代码的显式捕捉和处理,进而会导致严重的 Connection 不关闭、Transaction 不回滚、DB 中出现脏数据等情况。
所以需要将 SQLException 定义为检测异常,才会驱使开发人员去显式捕捉,并且在代码产生异常后清理资源。
<font color="#dd0000">对代码层次结构的污染</font><br />
举例:
public Man retrieve(Long id) throw SQLException {
//根据 ID 查询数据库
}
上面这段代码咋一看没什么问题,但是从设计耦合角度仔细考虑一下,这里的 SQLException 污染到了上层调用代码,调用层需要显式的利用 try-catch 捕捉,或者向更上层次进一步抛出。根据设计隔离原则,我们可以适当修改成:
public Man retrieve(Long id) {
try{
//根据 ID 查询数据库
}catch(SQLException e){
//利用非检测异常封装检测异常,降低层次耦合
throw new RuntimeException(SQLErrorCode, e);
}finally{
//关闭连接,清理资源
}
}
<font color="#dd0000">将异常包含在循环语句块中</font><br />
for(int i=0; i<100; i++){
try{
}catch(XXXException e){
//….
}
}
这么做最主要的问题在于是处理异常消耗系统资源太多。
<font color="#dd0000">利用 Exception 捕捉所有潜在的异常</font>
有时候懒得捕捉所有异常,就利用基类 Exception 捕捉所有潜在的异常:
public void retrieveObjectById(Long id){
try{
//…抛出 IOException 的代码调用
//…抛出 SQLException 的代码调用
}catch(Exception e){
//这里利用基类 Exception 捕捉的所有潜在的异常,如果多个层次这样捕捉,会丢失原始异常的有效信息
throw new RuntimeException(“Exception in retieveObjectById”, e);
}
}
这么做的坏处是:丢失了发生其他异常的具体信息。
<font color="#dd0000">在你的方法里抛出不具体的检查性异常</font>
public void foo() throws Exception { //错误方式
}
<font color="#dd0000">“早throw晚catch”原则</font>
应该尽快抛出(throw)异常,并尽可能晚地捕获(catch)它。 你应该等到你有足够的信息来妥善处理它。
自定义异常
的方法
- 如果希望写一个检查性异常类,则需要继承 Exception 类。
- 如果希望写一个运行时异常类,那么需要继承 RuntimeException 类。
class MyException extends Exception{
}
举例:
class MyException extends Exception {
private int detail;
MyException(int a){
detail = a;
}
public String toString(){
return "MyException ["+ detail + "]";
}
}
public class TestMyException{
static void compute(int a) throws MyException{
System.out.println("Called compute(" + a + ")");
if(a > 10){
throw new MyException(a);
}
System.out.println("Normal exit!");
}
public static void main(String [] args){
try{
compute(1);
compute(20);
}catch(MyException me){
System.out.println("Caught " + me);
}
}
}
运行结果:
Called compute(1)
Normal exit!
Called compute(20)
Caught MyException [20]
网友评论