异常与捕获

异常体系:当程序出现错误时能最大化减少损失的一种保护手段。

在Java中一切皆对象,所以异常也是类。

  • 先来看一下异常的继承类结构:
    在这里插入图片描述
    只有Throwable及其子类能够进行异常捕获与处理,所有的异常都是由继承它而来的。

  • Error:描述JVM运行时的内部错误和资源耗尽错误,如栈溢出堆溢出。应用程序不抛出此类异常,这种内部错误一旦出现,除了告知用户并使程序安全终止之外,再无能无力,这种情况很少出现。

  • Exception:程序中普遍存在的由于代码问题产生的错误。在它之下又分为两个分支,RuntimeExceptionIOException

由于程序错误导致的异常属于RuntimeException;而如果程序本身没有问题,但由于像I/O错误这类问题导致的异常属于IOException。

  • IOException:由于IO(输入输出)产生的异常,如在程序中打开了一个并不存在的文件。

  • RuntimeException:发生在运行时异常,如数组越界异常类型转换异常空指针(NPE)

非受查异常Error、RuntimeException及其子类,不强制用户进行异常处理
当异常产生时,尽量保证异常之后的代码可以正常运行。

受查异常:除了非受查异常的所有异常类都属于受查异常,强制用户进行异常处理。

1. 异常产生

  • 观察正确代码:
package yichang;

public class Test {
    public static void main(String[] args) {
        System.out.println("数学计算开始前");
        System.out.println("数学计算进行中"+10/2);
        System.out.println("数学计算结束后");
    }
}

在这里插入图片描述

  • 观察有错误的代码:
package yichang;

public class Test {
    public static void main(String[] args) {
        System.out.println("数学计算开始前");
        //零不能做被除数,除0异常
        System.out.println("数学计算进行中"+10/0);
        System.out.println("数学计算结束后");
    }
}

在这里插入图片描述
现在程序之中产生了ArithmeticException异常,但是在异常语句产生之前的语句可以正常执行完毕,而异常产生之后程序直接进行了结束。为了保证程序出现异常后尽量保证异常之后的代码可以继续执行,这时就需要异常处理。

2. 异常处理

异常处理语法格式:

try 
	[catch...]
	[catch...]
	...
	[finally]

对于以上三个关键字可以出现的组合有:

1. try...[1...N]catch...
2. try...finally
3. try...[1...N]catch...finally

try:放所有可能出现异常的代码
catch:当相应异常出现时捕获该异常,然后自定义处理方式
finally:保证重点代码(如IO流的关闭,JDBC资源的释放以及网络连接的关闭)一定会被执行的机制,无论是否产生异常,finally代码块中的内容一定会被执行。除了系统退出的情况System.exit()

  • 代码出现异常后使用try..catch进行处理:
package yichang;

public class Test {
    public static void main(String[] args) {
        System.out.println("数学计算开始前");

        try {
            System.out.println("数学计算进行中"+10/0);
        } catch (ArithmeticException e) {
            System.out.println("异常已经被处理了!");
        }
        System.out.println("数学计算结束后");
    }
}

在这里插入图片描述
出现了异常之后,由于存在异常处理机制,依然可以正常执行完毕。
以上代码虽然进行了异常处理,但是存在一个问题:你现在根本不知道程序产生了什么样的异常。所以为了明确的取得异常信息,可以直接输出异常类对象,或者调用所有异常类中提供的printStackTrace()方法进行完整异常信息
的输出。

package yichang;

public class Test {
    public static void main(String[] args) {
        System.out.println("数学计算开始前");

        try {
            System.out.println("数学计算进行中"+10/0);
        } catch (ArithmeticException e) {
            e.printStackTrace();
        }
        System.out.println("数学计算结束后");
    }
}

在这里插入图片描述

  • 使用try..catch..finally进行处理:
package yichang;

public class Test {
    public static void main(String[] args) {
        System.out.println("数学计算开始前");

        try {
            System.out.println("数学计算进行中"+10/0);
        } catch (ArithmeticException e) {
            e.printStackTrace();
        } finally {
            System.out.println("啦啦啦");
        }
        System.out.println("数学计算结束后");
    }
}

在这里插入图片描述
不管此时是否产生异常,最终都要执行finally程序代码,所以finally会作为程序统一出口。

以上程序是直接固定好了两个数字进行除法运算,现在通过初始化参数来进行除法运算。

  • 初始化参数进行数学运算:
package yichang;

public class Test {
    public static void main(String[] args) {
        System.out.println("数学计算开始前");

        try {
            //parseXX:String类转换为基本数据类型
            int x = Integer.parseInt(args[0]);
            int y = Integer.parseInt(args[1]);
            System.out.println("数学计算进行中"+x/y);
        } catch (ArithmeticException e) {
            e.printStackTrace();
        } finally {
            System.out.println("啦啦啦");
        }
        System.out.println("数学计算结束后");
    }
}

在这里插入图片描述
此时会存在如下问题:
① 用户没有输入初始化参数:ArrayIndexOutOfBoundsException
② 用户输入的不是数字: NumberFormatException
③ 被除数为0:ArithmeticException

以上代码我们发现,通过catch捕获异常的时候如果没有捕获指定异常,程序依然无法进行处理,现在最直白的解决方法就使用多个catch。

  • 多个catch块:
package yichang;

public class Test {
    public static void main(String[] args) {
        System.out.println("数学计算开始前");

        try {
            int x = Integer.parseInt(args[0]);
            int y = Integer.parseInt(args[1]);
            System.out.println("数学计算进行中"+x/y);
        } catch (ArithmeticException e) {
            e.printStackTrace();
        } catch (ArrayIndexOutOfBoundsException e) {
            e.printStackTrace();
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } finally {
            System.out.println("啦啦啦");
        }
        System.out.println("数学计算结束后");
    }
}

在这里插入图片描述
问题又来了,ArrayIndexOutOfBoundsException、NumberFormatException和ArithmeticException都是RuntimeException的子类,那么应该直接用RuntimeException来接收也是没问题的,避免了使用多个catch。

package yichang;

public class Test {
    public static void main(String[] args) {
        System.out.println("数学计算开始前");

        try {
            int x = Integer.parseInt(args[0]);
            int y = Integer.parseInt(args[1]);
            System.out.println("数学计算进行中"+x/y);
        } catch (RuntimeException e) {
            e.printStackTrace();
        } finally {
            System.out.println("啦啦啦");
        }
        System.out.println("数学计算结束后");
    }
}

问题是真这么写,真这么测试,不使用异常处理也一样,因为完全可以使用if..else判断。如果要想更好的处理异常,必须清楚异常的处理流程。

考点:finally

package yichang;

public class Test {
    public static void main(String[] args) {
        System.out.println(test());
    }
    public static int test() {
    	try {
    		System.out.println(10/2);
    		return 0;
    	} catch(ArithmeticException e) {
    		return 1;
    	} finally {
    		return 2;
    	}
    }
}
//结果到底是0还是2??结果是2!!!finally一定会被执行!!!
package yichang;

public class Test {
    public static void main(String[] args) {
        System.out.println(test());
    }
    public static int test() {
    	try {
    		System.out.println(10/0);
    		return 0;
    	} catch(ArithmeticException e) {
    		return 1;
    	} finally {
    		return 2;
    	}
    }
}
//结果是2,finally也会被执行
  • finally代码块一定会被执行,唯一除了以下情况:系统退出
package yichang;

public class Test {
    public static void main(String[] args) {
        try {
    		System.out.println("1");
    		System.exit(0);
    	} catch(ArithmeticException e) {
    		System.out.println("2");
    	} finally {
    		System.out.println("3");
    	}
    }
}
//输出1

3. throws关键字

用在方法声明上,明确表示该方法可能会产生异常。当异常产生不希望处理时,使用throws将异常对象抛回给调用者。

package yichang;

public class Test {
    public static void main(String[] args) {
        try {
            System.out.println(caculate(10, 0));
        } catch (ArithmeticException e) {
            e.printStackTrace();
        }
    }
    public static int caculate(int x, int y) throws ArithmeticException {
        return x/y;
    }
}

在这里插入图片描述
主方法本身也属于一个方法,所以主方法上也可以使用throws进行异常抛出,这个时候如果产生了异常就会交给JVM处理。

package yichang;

public class Test {
    public static void main(String[] args) throws Exception {
        System.out.println(caculate(10, 0));
    }
    public static int caculate(int x, int y) {
        return x/y;
    }
}

在这里插入图片描述

4. throw关键字

用在方法中,表示人为进行异常对象的产生。一般与自定义的异常类搭配使用。如果现在异常类对象实例化不希望由JVM产生而由用户产生,就可以使用throw来完成。

package yichang;

public class Test {
    public static void main(String[] args){
        test();
    }
    public static void test() {
        throw new RuntimeException("抛个异常玩玩");
    }
}

在这里插入图片描述

面试题:请解释throw和throws的区别 。
① throw用于方法内部,主要表示手工异常抛出。
② throws主要在方法声明上使用,明确告诉用户本方法可能产生的异常,同时该方法可以不处理此异常。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值