天天看点

黑马程序员----面向对象---异常

[size=small][align=center]

-------[url]http://www.itheima.com[/url]java培训、android培训期待与您交流!-------

[/align]

继续学习面向对象,,,,,

八、异常

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的数,这时有可能出现问题。所以,要定义异常处理方式来避免问题的发生。

[align=center]-------[url]http://www.itheima.com[/url]java培训、android培训期待与您交流!------- [/align][/size]