在理想世界中,用户输入数据的格式永远正确,选择打开的文件也一定存在,代码永远不会出现bug。但在现实世界中却充满了不良的数据和有问题的代码,试想一下如果费劲心思写完了一个非常冗杂的代码,但是却出现了不间断的异常,是多么的感到不适。这时我们就需要了解自己的代码,对可能出现的bug和错误加以提示,以便后来的调试与修改。在Java中提供了一些官方的异常申明,当然也可以自定义异常并且加以处理,下面详细说明一下我对于异常的理解和看法。
一:异常的概念:
在Java中,将程序执⾏过程中发⽣的不正常⾏为称为异常,下面举几个例子说明:
1.算数异常
public static void main(String[] args) {
System.out.println(10 / 0);
}
运行时出现:
2.数组越界异常:
public static void main(String[] args) {
int[] array = {1, 2, 3};
System.out.println(array [100]);
}
运行时的结果:
从以上的结果可以看出Java中的每一种异常都有对应的类来表示出来。
二:异常的分类:
在Java程序设计语言中异常对象都是派生于Throwable类的一个类的实例,如果Java中内置的异常类不能满足需求,用户还可以创建自己的异常类。
······编译时异常:
我们知道程序都是先编译后执行的,在程序的编译阶段出现的异常叫做编译时异常,也叫做受检查异常。(Checked Exception)
······执行时异常:
在程序的执行过程中产生的异常叫做执行时异常,也叫做非受检查异常。(Unchecked Exception)
三:异常的处理:
1.声明检查型异常:
在声明时使用throws关键字,throws 关键字⽤于在⽅法声明中列出该⽅法可能抛出的异常,它告诉调⽤者这个⽅法可能会抛出某些异常,调⽤者需要处理这些异常。使⽤ throws 实际上是将异常的处理责任转移给了调⽤该⽅法的代码。
/*
FileNotFoundException : 编译时异常,表明⽂件不存在
此处不处理,也没有能⼒处理,应该将错误信息报告给调⽤者,让调⽤者检查⽂件名字是否给错
误了
*/
public void OpenConfig(String filename) throws FileNotFoundException {
if (filename.equals("config.ini")) {
throw new FileNotFoundException("配置⽂件名字不对");
}
// 打开⽂件
}
其中代码第一行就是使用throws关键词来对可能出现的异常的声明,代码第三行使用了throw关键字抛出了一个指定的异常用来提醒调用者。
注意:
1.声明的异常必须是 Exception 或者 Exception 的⼦类
2.⽅法内部如果抛出了多个异常,throws之后必须跟多个异常类型,之间⽤逗号隔开,如果抛出多个异常类型具有⽗⼦关系,直接声明⽗类即可。
3.调⽤声明抛出异常的⽅法时,如果该异常是编译时异常/受查异常时,调⽤者必须对该异常进⾏处 理,或者继续使⽤throws抛出。
在IDEA编译器中,对异常进行处理时可以使用alt+enter快捷键出现如上图所示两种处理结果
关于throw的用法,详见下列代码(实现⼀个获取数组中任意位置元素的⽅法。)
public static int getElement(int[] array, int index){
if(null == array){
throw new NullPointerException("传递的数组为null");
}
if(index < 0 || index >= array.length){
throw new ArrayIndexOutOfBoundsException("传递的数组下标越界");
}
return array[index];
}
public static void main(String[] args) {
int[] array = {1,2,3};
getElement(array, 3);
}
倒数第二行代码调用方法getElement时,很明显下标越界了,这时手动抛出了一个异常后编译器会识别出来:
在程序设计时,一个方法必须声明所有可能抛出的检查型异常,而非检查型异常要么在你的控制之外,要么是由一开始就应该避免的情况导致的。
2.异常的真正处理(try-catch语句的用法)
语法格式:
try{
// 将可能出现异常的代码放在这⾥
}catch(要捕获的异常类型 e){
// 如果try中的代码抛出异常了,此处catch捕获时异常类型与try中抛出的异常类型⼀致
时,或者是try中抛出异常的基类时,就会被捕获到
// 对异常就可以正常处理,处理完成后,跳出try-catch结构,继续执⾏后序代码
}[catch(异常类型 e){
// 对异常进⾏处理
}finally{
// 此处代码⼀定会被执⾏到
}]
// 后序代码
// 当异常被捕获到时,异常就被处理了,这⾥的后序代码⼀定会执⾏
// 如果捕获了,由于捕获时类型不对,那就没有捕获到,这⾥的代码就不会被执⾏
注意:
1. []中表⽰可选项,可以添加,也可以不⽤添加
2. try中的代码可能会抛出异常,也可能不会
public static void main(String[] args) {
int[] array = {1, 2, 3};
try {
System.out.println("before");
// array = null;
System.out.println(array [100]);
System.out.println("after");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("这是个数组下标越界异常");
e.printStackTrace();
} catch (NullPointerException e) {
System.out.println("这是个空指针异常");
e.printStackTrace();
}
System.out.println("after try catch");
}
运行结果:
catch (ArrayIndexOutOfBoundsException | NullPointerException e) {
...
}
如果异常之间具有⽗⼦关系,⼀定是⼦类异常在前catch,⽗类异常在后catch,否则语法错误:
三.自定义异常的实现:
class UserNameException extends Exception {
public UserNameException(String message) {
super(message);
}
}
class PasswordException extends Exception {
public PasswordException(String message) {
super(message);
}
}
在使用时可以直接调用自定义异常,与官方给定的异常使用方法一样。