在开发的过程中,有时程序出现的不可避免,不可预测的错误发生,我们称之为异常,异常一旦你出现并没有进行合理处理的话,那么程序就将中断进行。
范例1(没有异常的程序):
public class TestDemo {
public static void main(String[]args){
System.out.println("除法");
int result = 10/2;
System.out.println("计算结果:"+result);
System.out.println("计算结束");
}
}
运行结果:
除法
计算结果:5
计算结束
范例2(产生异常的程序):
package com.wfg.demo;
/**
* @Author WFG
* @Date 2019/5/29 20:48
*/
public class TestDemo {
public static void main(String[]args){
System.out.println("除法");
int result = 10/0;
System.out.println("计算结果:"+result);
System.out.println("计算结束");
}
}
运行结果:
除法
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.wfg.demo.TestDemo.main(TestDemo.java:11)
对比上述两个范例,可以发现,异常语句一旦产生,之后的语句将不再执行,默认情况下是将异常信息输出,而后自动结束程序的执行。
而我们要做的是:即使程序出现了异常,也要程序正确的执行完毕。所以就引入了异常处理
1、try...catch...finally语句
语法格式如下:
try {
可能出现异常的语句;
}[catch (异常类型 异常对象){
处理异常;
}] [finally {
程序块(finally语句块是异常处理结构最后执行的部分,无论try如何退出
都将执行此语句块);
}]
范例1(异常处理):
package com.wfg.demo;
/**
* @Author WFG
* @Date 2019/5/29 20:48
*/
public class TestDemo {
public static void main(String[]args){
System.out.println("除法");
try {
int result = 10/0;
System.out.println("计算结果:"+result);
} catch (ArithmeticException e) {
System.out.println(e);
}
System.out.println("计算结束");
}
}
运行结果:
除法
java.lang.ArithmeticException: / by zero
计算结束
分析上例,加入了异常处理后,程序即使有了异常,程序也可以正常的执行完毕,但是异常处理时的错误输出信息和之前对比,出错的信息不明确了,那么为了让错误信息更加的完整,一般会调用printStackTrace()方法进行异常的打印,以下方法的打印的异常信息是最完整的
范例2(printStackTrace方法打印完整错误信息):
package com.wfg.demo;
/**
* @Author WFG
* @Date 2019/5/29 20:48
*/
public class TestDemo {
public static void main(String[]args){
System.out.println("除法");
try {
int result = 10/0; //异常
System.out.println("计算结果:"+result); //之前语句有异常,此句不在执行
} catch (ArithmeticException e) {
e.printStackTrace(); //异常处理:输出错误信息
}
System.out.println("计算结束");
}
}
运行结果:
除法
计算结束
java.lang.ArithmeticException: / by zero
at com.wfg.demo.TestDemo.main(TestDemo.java:12)
除了try...catch格式处理异常外,还可以使用try...catch...finally:
范例3(异常处理):
package com.wfg.demo;
/**
* @Author WFG
* @Date 2019/5/29 20:48
*/
public class TestDemo {
public static void main(String[]args){
System.out.println("除法");
try {
int result = 10/0;
System.out.println("计算结果:"+result);
} catch (ArithmeticException e) {
System.out.println(e);
}finally {
System.out.println("不管出不出现异常都执行");
}
System.out.println("计算结束");
}
}
运行结果:
除法
java.lang.ArithmeticException: / by zero
不管出不出现异常都执行
计算结束
以上例子所执行的数学运算,两个参数都是由程序默认提供,那么如果说现在计算的两个参数都是通过初始化参数传递,会不会出现问题呢?
范例4:
package com.wfg.demo;
/**
* @Author WFG
* @Date 2019/5/29 20:48
*/
public class TestDemo {
public static void main(String[]args) {
System.out.println("除法");
try {
int x = Integer.parseInt(args[0]); //接收参数
int y = Integer.parseInt(args[1]);//接收参数
int result = x/y;
System.out.println("计算结果"+result);
} catch (ArithmeticException e) {
e.printStackTrace();
} finally {
System.out.println("不管是否出现异常都执行");
}
System.out.println("计算结束");
}
}
运行结果:
除法
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
不管是否出现异常都执行
at com.wfg.demo.TestDemo.main(TestDemo.java:13)
分析以上例子:
数据由外部传送,那么就有可能出现以下问题:
(1)执行时不输入参数,ArrayIndexOutOfBoundsException,未处理;
(2)输入的参数不是数字,NumberFormatException,未处理;
(3)被除数为0,ArithmeticException,已处理。
可以发现,以上的程序实际上是存在三种异常,而程序中只能处理一种,而对于不能处理的异常,发现程序会直接中断执行。我们可以通过添加多个catch的方法来解决此类问题。
范例4(加入多个catch):
package com.wfg.demo;
/**
* @Author WFG
* @Date 2019/5/29 20:48
*/
public class TestDemo {
public static void main(String[]args) {
System.out.println("除法");
try {
int x = Integer.parseInt(args[0]); //接收参数
int y = Integer.parseInt(args[1]);//接收参数
int result = x/y;
System.out.println("计算结果"+result);
} catch (ArithmeticException e) {
e.printStackTrace();
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
} catch (NumberFormatException e) {
e.printStackTrace();
}
finally {
System.out.println("不管是否出现异常都执行");
}
System.out.println("计算结束");
}
}
上面已经完成了异常的基本处理,但是所有的异常像之前那样添加多个catch一条条的判断似乎是不可能的任务,因为我们今后可能会碰见各种各样的异常,包括很多没有见过的异常。所以我们必须研究异常的流程和结构。
查看两个异常类的继承结构:
(2)ArrIndexOutOfBoundException:
可以发现,所有的异常类型最高的继承类是Throwable,Throwable下有两个子类:
(1)Error:指的是·JVM错误,这个时候的程序并没有执行,无法处理;(少见)
(2)Exception:指的是程序中出现的错误信息,可以进行异常处理。(RuntimeException就是一个常见的错误可catch可不catch,属于编程导致的错误,比如:数组访问越界、访问空指针等等)
通过继承关系发现,在进行处理异常的时候以Exception为主,并且可以进行以下的异常处理流程:
1、如果程序发生了异常,那么JVM根据异常的类型,实例化一个指定的异常类的对象;
2、如果这时候程序中没有任何的异常处理操作,则这个异常的实例化对象将交给JVM进行处理,而JVM的默认处理方式就是进行异常信息的输出,而后中断程序;
3、如果程序存在了异常处理,则会由try语句捕获产生的异常对象;
4、与try之后的每一个catch进行匹配,如果匹配成功,则使用指定的catch进行处理,如果没有匹配成功,则向后面的catch继续匹配,如果没有任何的catch匹配成功,则这个时候交给JVM进行默认处理;
5、不管是否有异常都会执行finally程序,如果此时没有异常,执行完finally,则会执行程序之中其他代码,如果此时有异常没能够处理(没一个catch可以满足),那么也会执行finally,但执行完finally后,将默认交给JVM进行异常信息的输出,并且程序中断。
通过上面的分析可以发现。实际上catch捕获异常类型的操作,就和方法接收参数是一样的,从多态角度来讲,所有的异常都是Exception的子类,那么实际上所有的异常都可以使用Exception进行接收:
范例:
package com.wfg.demo;
/**
* @Author WFG
* @Date 2019/5/29 20:48
*/
public class TestDemo {
public static void main(String[]args) {
System.out.println("除法");
try {
int x = Integer.parseInt(args[0]); //接收参数
int y = Integer.parseInt(args[1]);//接收参数
int result = x/y;
System.out.println("计算结果"+result);
} catch (Exception e) {
e.printStackTrace();
}
finally {
System.out.println("不管是否出现异常都执行");
}
System.out.println("计算结束");
}
}
但这种操作也存在一些问题:如果在一些异常处理要求严格的项目中,异常必须分别处理,这时Exception就不是那么好用了。
如果异常处理不是很严格直接编写Exception即可。