Java的异常处理概述
防错程序设计是一个优秀程序中不可或缺的一部分,一个好的软件,应该将错误通过自身的程序进行消化,而不是将错误抛给用户。因此,程序中要用到异常处理。Java看待异常的思想是将异常看成是异常类的对象来处理。
异常的概念
异常也就是导致程序不能正常执行的非正常情况,可以导致程序发生异常的情况有许多,Java自身也定义了一些异常类,比如:ArrayIndxOutOfBoundsException(数组下标越界),ArithmeticException(算数运算错误,如被0除),ClassNotFoundeException(未找到装载的类),NullPointerException(空指针访问),NumberFormatException(数字格式错误)等。
1.异常的简介
异常可以分为:检查性异常和运行时异常。前者是在程序编写时,系统就自动检测到了异常,后者是在程序编译完成后,系统出现的异常,常见的是由于用户输入的内容不符合要求而产生的异常。
【举例】
public class Test {
public static void main(String[] args) {
int a=1;
int b=0;
System.out.println(a/b); //被除数不能为0
}
}
上面的代码属于运行时异常,也就是在程序运行后,系统才显示出异常,显示的结果为:Exception in thread "main" java.lang.ArithmeticException: / by zero at com.blog.Test.main(Test.java:6)。也就是改程序有ArithmeticException异常。
那么如何修改程序已达到异常处理的目的呢?Java提供了几种程序块来处理异常或可能有异常的代码。它们分别是try,catch,finally,throw,throws。
那么以上代码改如何修改系统才不会报出异常呢?可以利用try{}catch(){}来对出现异常的代码块进行处理。修改结果如下:
public class Test {
public static void main(String[] args) {
int a=1;
int b=0;
try{
System.out.println(a/b);
}catch (ArithmeticException e){
System.out.println("被除数不可为0");
}
}
}
输出结果为:被除数不可为0。此外,IDEA有快速生成try catch的快捷键,步骤是:选中要异常处理的代码块,然后按住Ctrl+Alt+T,就会在出现的列表中发现try catch程序块。
2.异常的类层次
Java的异常类是处理异常时错误的特殊类,每一种异常对应一种特定的运行错误。所有的异常类都是系统类库中Exception类的子类,其继承结构如下:
-
Throwable类:是异常类层次中的最高层,其中第一了一个异常描述串以及可获取该描述的getMessage()方法。
-
-
Exception类:该类就是我们要进行处理的异常类。
异常处理结构
进行异常处理必须使用try程序块,将可能产生错误的代码放在try块中,当JVM执行时发现异常时,会立即停止执行后续代码,然后查找异常处理块,对catch块按次序进行匹配检查,一但找到一个匹配者,就执行该catch块中的代码,且不再检查后面的catch块。如果try块中没有异常,则忽略catch块。
1.具体格式:
try{
语句块;
}catch(异常类型 参变量名){语句块;}
finally{语句块;}
【说明】
-
可以把try看成是提醒系统,在try块中的程序可能会出现异常,try块后可接多个catch块。
-
一旦发现异常,try中的剩余语句不再执行。
-
catch语句块用于捕捉和处理异常对象。当try块出现异常时,运行系统会将异常对象传递给catch中的参变量,在catch块中可以通过该对象获取具体的异常信息。
-
finally部分是可以没用的,但如果存在,则无论异常是否发生,finally部分的语句必须执行。即便是try或catch中含有退出方法的语句return,也不能阻止finally中代码块的执行。
2.throw与throws
一般情况下,程序运行过程中发现异常时,系统就会产生一个含有异常的描述信息和异常的位置的异常对象,产生异常对象的过程就叫做异常的抛出(throw)。当产生异常对象后,若为编写异常处理的代码,系统将自动返回异常对象的信息,即异常的描述和位置。而要使程序严密且好用,那么我们就需要对异常进行处理,以接受和处理异常对象。
-
throw
系统自定义的运行异常都可由系统在运行时自动抛出,这种异常抛出方式称为隐式抛出。而自己设计的异常,需要我们自己通过throw语句抛出。
【例】抛出MyException异常
class MyException extends Exception{//自定义的异常类
String id;//异常标识
public MyException(String str){//异常类的构造方法
id=str;
}
public String toString(){
return("异常:"+id);
}
}
public class Test {
public static void main(String[] args) {
try{
throw new MyException("输入错误");//抛出一个异常
}catch (MyException e){//捕捉并处理异常
System.out.println(e);
}
}
}
输出结果:异常:输入错误
【说明】
(1)一个throw语句只能抛出一个异常对象。
(2)throw语句会改变程序的执行流程。一般来说,执行完throw语句后会结束其所在方法的执行。
(3)throw语句必须是某个语法结构的最后一条语句。当throw后面还有语句时,会显示错误:该代码为不可达的代码
-
throws
在方法的声明部分可以加上throws子句——告知JRE该方法可能会抛出某些异常。格式:
public void doSomething() throws IOException, ArithmeticException
{
//方法体
}
【说明】
关键字throws后面可以跟多个异常,逗号隔开,没有先后顺序,甚至可以是方法体中并未抛出的异常。
-
throw语句与throws子句区别:一个是抛出异常,另一个是声明方法中将产生什么异常。
自定义异常
看到自定义异常,我就有个疑问:为什么系统已经有了异常处理类还要来搞个自定义异常呢?估计老师上课时讲过,估计我上课时走神了。那么原因是什么呢?其实,稍微想一想就会知道大概原因:由于异常的情况是千奇百怪的,而系统给我们提供的异常类却是有限的,所以当我们的catch无法捕捉这个系统中没有的异常类型时,我们就需要自己来设计一个异常类,来返回具体的异常信息,比如说异常的类型或异常代码块所处的位置。
1.自定义异常类设计
自定义的异常类要通过继承Exception类来实现,自定义异常类中一般包括异常标识,构造方法和toString()方法。这三者的作用分别是:记录异常代码块的信息,给异常标识赋值,输出描述的异常。
【例】一个简单的自定义异常类
class MyException extends Exception{
String id;//异常标识
public MyException(String str){
id=str;
}
public String toString(){
return("异常:"+id);
}
}
【说明】自定义的异常类和普通类一样,都是可以被继承的。前面我们说过,catch捕获异常的顺序按的是catch块由上到下的次序,一但找到匹配的catch块,后面的catch块不再匹配。所以,我们应该按照由子类到父类的顺序来捕获异常。
2.抛出异常
异常的本质是对象,所以throw关键字后面跟的是new运算符来创建一个异常对象。 不管是自定义的还是系统本身的异常对象,都需要由try catch语句块来对异常进行划分区域,捕捉和处理。而自己定义的异常类,需要在try语句块的末尾,通过throw来创建异常对象(可以创建多个自定义异常类,但要注意一个throw只能创建一个),并由catch对try中的所有可能出现的异常对象进行捕捉和处理。
3.方法的异常声明
如果某一方法中有异常抛出,有两种选择:一是在方法内对异常进行捕捉处理,;二是在方法中不处理异常,将异常处理交给外部调用程序,通常在方法头使用throws子句列出方法可能出现哪些异常。
习题
【例1】设计一个方法计算一元二次方程的根
//设计一个方法计算一元二次方程的根
public class Ex9_1 {
static double[] root(double a,double b,double c) throws IllegalArgumentException {
//此为一个求一元二次方程根的方法,在方法声明后,有throws子句声明的异常类
double[] x = new double[2];
if (a == 0)
throw new IllegalArgumentException("a 不能为0");//通过throw抛出异常,在新建的异常类的括号中,输入异常标识。
else {
double dic = b * b - 4 * a * c;
if (dic < 0)
throw new IllegalArgumentException("此方程无根");//抛出此异常后,此方法的后续代码不再执行
x[0] = (-b + Math.sqrt(dic) / (2 * a));
x[1] = (-b - Math.sqrt(dic) / (2 * a));
return x;
}
}
public static void main(String[] args) {
try{
double x[]=root(0,5,3);//创建一个double类型的数组,并通过root方法的返回值对该数组进行赋值
System.out.println("方程的根为:"+x[0]+"和"+x[1]);
}catch (IllegalArgumentException e){
System.out.println(e);//返回该异常对象的信息
}
}
}
输出结果:java.lang.IllegalArgumentException: a 不能为0
总结
异常处理的作用主要有两点:(1)通过程序自身来处理异常(2)让程序遇到异常时仍能继续执行其他代码。总之,都是为了提高用户对程序的使用体验。在catch语句块中,不只是可以将异常的信息返回,还可以根据异常的类型,对程序进行修改,使得异常被处理。此外,在多重catch块中,最好加上一个catch(Exception e)以避免有异常情况被忽略。

浙公网安备 33010602011771号