------- http://www.itheima.comjava培训、android培训期待与您交流!-------
继续学习面向对象,,,,,
八、异常
1、概述
异常就是程序在运行时出现的不正常情况。
在现实生活中,我们也会遇到很多不正常情况。其中有的是可以解决的,有的则没有解决必要。比如,电脑的运行速度慢,我们可以清理清理电脑灰尘或对电脑进行垃圾清理。但是,要是,某天不小心将电脑摔地上了,如果幸运,可以修修,如果不幸运,那么,对不起,就不用修了。
而在java语言中,也有对问题的描述。因为问题也可以归结为一类事物,那么,就可以通过java类的形式对问题进行描述,并封装成对象,也就是说,异常时java对不正常情况进行描述后的对象体现。
问题分为两种:严重的问题:Error 一般不编写针对性的代码对其进行处理
非严重的问题:Exception 可以针对性处理
而对于这些问题,Error或Exception,都有一些共性内容,如,不正常情况的信息,引发原因等。所以,将这些共性内容向上抽取就形成了一个体系:
可抛的:Throwable:Error/Exception
2、异常的处理
(1)使用 try - catch - finally 语句进行异常处理
try { 需要被检测的代码 }catch( 异常类 变量 ){ 处理异常的代码(处理方式) }finally{ 一定 会被执行的语句 }
下面用一个例子解释:
首先定义一个简单的算数运算,求两个数的商(不考虑小数)。我们知道,在进行除法运算时,除数不能为0。所以,我们要遵循这个规则。如果出现了除数为0 ,则报异常,否则程序继续运行。
//定义一个类,用于封装除法运算 class Div { //定义一个方法,用于计算两个数的商 int div(int x,int y) { return x/y; } } class ExceptionDemo { public static void main(String[] args) { Div d=new Div(); try { int x=d.div(4,0); System.out.println("x="+x); } catch (Exception e)//Exception e=new ArithmeticException(); //父类引用指向子类对象 多态的运用 { System.out.println("除零了,不符合规则"); System.out.println(e.getMessage());//获取异常的信息 e.printStackTrace();//打印异常的堆栈的跟踪信息 } System.out.println("over"); } }
分析一下处理异常的流程:
Div d=new Div();在内存中创建对象
int x=d.div(4,0);调用div方法,这时,将4 0传给x、 y,由于除数为0,不符合规则,虚拟机检测到异常(java语言自定义的异常),那么就会出现ArithmeticException,出现了异常,下面的程序就不会执行,将异常转加给catch语句,让他对算法异常进行处理。Exception e=new ArithmeticException(); 父类引用指向子类对象 多态的运用
然后,将处理的结果打印出来,这时,异常处理结束。接下来运行异常处理语句外的程序:
System.out.println("over");
(2)将异常声明(在函数体上声明异常),也就是抛出异常(throws)
还是上面那个例子。我们知道,x和y的值是用户输入的值,那么,传入的值就不明确。如果传入的是符合规则的,程序就不会有什么问题,但如果传入的是不合规则的,程序就会停掉。这是我们不希望的。
所以,我们在方法上将可能出现的问题通过关键字throws标识一下,这时编译时会出现错误信息:必须将异常捕获或声明。
一种方式是:在主函数上将异常抛出(这时是将异常抛给了JVM)
int div(int x,int y) throws Exception
一种方式是:在调用方法处捕获异常。
try - catch
声明异常便于极高安全性,让调用者进行处理,不处理则会失败
(3)多异常处理
显然,上面的例子只出现了一个异常。但在实际的开发过程中会出现很多异常。所以,要对多个异常进行处理。
A、也就是在函数上声明多个可能出现的异常(一般不同异常类用逗号","隔开),这 样会更具体地处理异常。
如,throws ArithmeticException ,ArrayIndexOutOfBoundException
那么在处理异常时或捕获或声明异常。
B、在函数上声明几个,那么就要有几个catch块,且声明的和处理的要一致。
要注意:若多个catch中出现继承关系,父类的异常catch要放在最下面(若放在最上面,出现的异常 父类会处理完,一下的catch块就不会执行,且报错)。
C、若捕获异常,则每个catch语句中要传入具体的异常类(函数上声明什么异常,
catch中就要处理什么异常),且处理的方法要具体,
不能输出默认的e.printStackTrace信息,也不能简单地打印自定义的异常信息。
D、当然,在实际开发中不会将出现的异常信息打印出来,这时会用文件记录每个异常发生的时间,情况等信息,存放在异常日志中。
(4)自定义异常
虽然java语言自定义了很多异常类,但是,在实际开发中我们还是会遇到很多意想不到的异常。这时,就需要我们定义属于我们自己的异常处理方式。
还是上面那个例子:java异常体系中只对除数为0的情况定义了异常。但如果除数为负数,这个运算方法也会出错。为了解决这个问题,就要自定义一个异常类,用于封装新出现的异常问题。
class FuShuException extends Exception { //函数一初始化就会有信息 FuShuException(String message){ //因为父类中已经定义了显示异常信息的方法,所以我们可以直接用 super(message); } } class Div { int div(int x,int y) throws FuShuException { if (y<=0) //手动通说关键字throw抛出自定义异常对象 throw new FuShuException("除数为负数"); return x/y; } }
在抛出自定义异常对象后,我们可以有两种解决方式:
A、在函数内部使用try - catch处理
B、在函数上声明
这里我们直接在函数上声明了
测试一下:
public static void main(String[] args) throws FuShuException { Div d=new Div(); int x=d.div(4,0); System.out.println("x="+x); }
3、RuntimeException
RuntimeException是java语言异常体系中比较特殊的一个异常类。为什么说他特殊呢?我们通过上面的例子解释一下:
class Div { int div(int x,int y) { if (y==0) throw new ArithmeticException("除数为0"); return x/y; } }
测试一下:
public static void main(String[] args) { Div d=new Div(); int x=d.div(4,0); System.out.println("x="+x); }
这里我们只是在函数内部将ArithmeticException这个异常对象抛出去,并没有在函数上声明,也没有在函数内部通过try - catch进行处理,但是在编译时(javac)并没有出现出错提示。这是为什么呢?
这里就涉及到ArithmeticException这个异常类的特点,
也就是RuntimeException的特殊之处:
A、若在函数内抛出该异常(这里所说的异常是RuntimeException异常及RuntimeException 的子类异常:空指针异常、算法异常、角标越界异常等),函数上可不用声明,编译能通过
B、若在函数上声明了该异常,调用者可不用进行处理,编译一样通过。
分析一下原因:
我们定义的这个算数方法是提供给用户使用的。用户并不知道他们输入的数值(这里只限数值)是怎样的。所以,输入合法的数值程序就会正常运行,若输入的数值不合法,那么JVM就会检测出输入的数值不合法,就会使用自定义的异常方式处理。但不需要将检测后的处理过程暴露给用户,只要将程序停止,那么用户就会意识到输入的数值不合法(不对),从而就会重新输入。
那么对于自定义异常,若该异常发生无法再继续进行运算,就让自定义异常继承RuntimeException。
异常分两种:
编译时检测异常(能处理的可标识出去)
运行时异常(编译时不被检测异常RuntimeException及其子类)
4、finally代码块
finally代码块中定义的是一定要执行的语句,同常用于关闭资源。
处理语句的其他搭配:
try{}catch(){}
try{}catch(){}finally{}
try{}finally{}
5、覆盖时的异常特点
我们知道,一般解决异常要么在函数上声明,要么在函数(方法)内抛出异常对象,要么在函数内使用try - catch进行处理。而函数(方法)具有覆盖的特点,那么,当子类覆盖父类的方法时,父类又有异常处理,子类在覆盖时应该注意那么问题呢?
(1)子类在覆盖父类方法时,如果父类中方法抛出异常,则子类在覆盖时抛出的异常必须是 父类方法抛出的异常或父类抛出异常的子类或者不抛。
(2)当父类方法中抛出多个异常时,子类覆盖父类方法时抛出的异常要能被父类处理(只能抛 出父类方法中异常的子集)
(3)如果父类或接口的方法中未抛出异常,则子类在覆盖方法时也不可抛出异常。
若此时子类发生了异常则必须进行try处理,且绝对不能抛(没有接的)
6、异常练习
异常可以将正常流程与出现的问题分离。
定义一个方法,用于计算圆(Circle)和长方形(Rectangular)的面积。由于计算的都是图形的面积,但是圆和长方形的面积公式不同,及实现的具体内容不同,可以定义一个接口,在接口中定义一个没有主体的方法,让子类具体实现(当然,也可以将计算面积的功能视为基本功能,定义抽象类和抽象方法,让子类复写父类的方法,并定义自己的具体计算方式)。
由于我们编写的程序是供用户使用的,他们不知道输入的内容具体是什么,如输入的数值是小于0的数,这时有可能出现问题。所以,要定义异常处理方式来避免问题的发生。
//定义异常 class NoValueException extends RuntimeException{ NoValueException(String message){ super(message); } } interface Shape { //因为计算图形的面积公式不同,且要传递的参数也不同, //计算要使用到图形自身的长、宽或半径 void getArea();//double getArea();无参 } class Rec implements Shape{ //定义长方形的属性 private int len,wid; //构造函数 初始化 Rec(int len,int wid) //throws NoValueException //继承RuntimeException 不用声明 { if (len<=0 || wid<=0) throw new NoValueException("非法值"); //程序结束 { this.len = len; this.wid = wid; } } //覆盖getArea()方法 public void getArea() { System.out.println("S(rec)="+len*wid); } } class Circle { private int radius; //全局常量, public static final double PI=3.14; Circle(int radius) { if (radius<=0) throw new RuntimeException("非法值");//NoValueException 提示不同 this.radius = radius; } public void getArea() { System.out.println("S(circle)="+radius*radius*PI); } } class ExceptionTest2 { public static void main(String[] args) { Rec r = new Rec(3,8);//当长方形的长或宽出现负数时,若不做任何处理,程序继续运行,并打印结果,不合逻辑。 //故添加异常处理 r.getArea(); Circle c = new Circle(-2); c.getArea(); //若此处处理,则程序继续运行到 over , /* try { Rec r = new Rec(-3,8); r.getArea(); } catch (NoValueException e) { System.out.println("over"); } */ } }
------- http://www.itheima.comjava培训、android培训期待与您交流!-------
[/size]