第16讲:异常处理
16.1、简介:
异常是程序执行时碰到的任何错误情况或意外行为。
以下这些情况都可以引发异常:您的代码或调用的代码(如共享库)中有错误,操作系统资源不可用,公共语言运行库碰到意外情况(如无法验证代码),等等
《Exception Handling for C++》关于异常处理论文,
向对象中每增加一个类,都可能包含一些错误。
Java使用和C++类似的异常处理
1、处理错误的方法有很多流行方法
2、一般方法是把程序处理代码段分散到系统代码中,在可能发生错误的地方处理错误
优点:便于程序员能够条理的看到程序中异常的处理
缺点:异常处理程序“搅乱”代码,程序员很难关心程序本身能否正常完成功能,而只关心了性能。
3、常见异常实例包括:数组下标越界,算法溢出(超出数值表达范围),除数为零,无效参数、内存溢出
异常处理功能:主要处理一些同步异常(除数为0),不宜处理一些异步事件(Disk I/O End、网络信息到达、点击鼠标、敲击键盘)这些最好使用java事件监听。
异常处理程序:能够让系统在出现异常的情况下恢复过来的程序
使用异常处理情况:异常诊断与异常处理不在同一个位置下时使用异常处理程序(若用户一直通过键盘与程序通话,那么就不能使用处理键盘输入处理)
使用异常的注重事项:
1、异常处理位置与异常发生位置必须不同(若一个程序能够处理自己的错误,那么就采用传统的错误处理方式进行局部处理)
2、要避免使用异常处理来代替错误处理,若这样就会降低程序的清楚性。
3、尽管可以使用异常处理来代替错误处理,但会降低性能
4、异常处理会占用程序的格外执行时间
5、异常处理能够提高程序的容错性
6、程序员使用JAVA标准的异常处理功能来代替他们的专用方法,可以在大型项目中提高程序的清楚性
7、异常是超类Exception继续的子类,主要如何处理“未扑获的异常”,无法预料的异常。
8、异常处理在java中原理:
异常处理有Method调用者的调用者或者Method调用者来处理,
9、异常处理适用分别开发的组件系统
10、因为一些程序员使用不支持异常处理语言编程时,往往拖延或忘记错误处理程序的编写,故,Java强制程序员从项目一开始就着手进行异常处理,程序员必须投入很大精力把异常处理的策略融合到软件产品中
11、最好在进行系统设计是就把异常处理融合在系统中,若系统一实现,就很难添加异常处理功能
16.2如何使用异常处理:
在以下情况下使用异常处理:
1、当方法因无法控制的原因而不能实现其功能时;
2、处理来自程序组件整体异常,这些程序组件不适宜直接处理异常
3、在大型项目中,对于每个项目都以一致的方式进行错误处理
4、在类库中出现每一个异常,都需要一个惟一错误处理,在类库中使用异常处理很合适
16.3 其他的错误处理技术:
处理异常的方法:
1、在程序可以忽略异常(忽略异常在大型公用软件和要害处理软件可能会导致重大的软件破坏,而自己用的软件,通常可忽略一些错误)
2、碰到异常时,程序可以根据提示来终止运行(但是处理要害任务是绝对不可以采用,〈神5返回仓温控系统〉?863项目。)
16.4 java异常处理的基础:
java的异常处理适用于在一个方法中能够检测出错误单不能处理错误的情况,这样方法将抛出一个异常(JAVA无法保证“存在”的异常处理程序能够处理某种异常,若“存在”,就“捕捉”异常,并处理“异常”,如找不到,那么:
命令行APP/控制台APP(不基于GUI),当异常不能被“捕捉”,那么该程序会运行默认异常处理程序,退出JAVA,若Applet或基于GUI的APP,当一个异常未被“捕捉”,GUI在运行默认异常处理程序依然会显示,而且用户使用的GUI处于一个不稳定的状态)
)
JAVA代码中:1、可能出现异常的代码-----{ …… try{抛出一个异常}-----程序块 ……catch1{异常处理程序1};……catch100 {异常处理程序100}; finally{无类是否出现异常都执行的程序}
1、若try抛出Exception,App寻找在Catch1~100寻找合适异常处理程序,若找到,执行CATCH{}代码,没有,执行最后一个catch{}后代码
2、若try未抛出Exception,就执行执行最后一个catch{}后代码。
3、throws子句来抛出指定的异常,异常抛出点(throws 语句位置、try{}程序块、try()程序块中直接和间接调用的方法中)
4、java采用终止方式异常处理,不是恢复方式的异常处理
5、发生异常时,异常四周信息(抛出对象本身类型)-------------异常处理程序
12.5一个异常处理的简单实例:除数为0
需求:使用一个小应用程序,完成两数相除的计算
分析:
我们使用有顶向下,逐步求精的方法:
1、首步我们一般在程序中先处理我们完成异常处理的类,完成异常处理的功能,因为可能除数为0是会出现异常,我们查找java.lang包中各个Exception类,发现RuntimeException类集合中的ArithmeticException可以处理运算异常,我们就让一个DivideByZeroException(继续于ArithmeticException) 更非凡类来专门处理除数为零的异常。在DivideByZeroException()中调用super()(专门初始化传如参数的方法)来传如异常处理后对象描述(初始状态)
2、第一步,创建一个Applet类完成完成两数相除的计算
3、第二步:处理对象与方法:声明4个GUI组件对象和1个用于存储计算结果(double)的和2个被除数与除数基本类型(int)
A、初始化对象方法init()
B、处理当除数键盘输入后,触发事件进行计算的方法(返回判定)action()
a、因为在此方法中,有可能出现除数为0的可能,而且在计算中才回出现错误,错误出现后需要进行处理异常,那么我在其中使用try{}程序块先调用方法quotient进行计算result=quotient(number1,number2),1、然后让quotient()方法(使用方法体中的throws Ex{}抛出异常程序块---此为深层嵌套)抛出异常(因为此处理信息比较简单,但存在信息传递,我们让其拖后处理)我们没有显式在try()块中抛出异常。有抛出异常就有获得异常,使用catch{}程序块完成,a)、在quotient()中(实际深层嵌套throws Ex{}抛出异常程序块),当除数=0时,就调用throw子句创建并抛出一个new catch{}“捕捉”(指定匹配类型DivideByZeroException) quotient()抛出异常信息(DivideByZeroException),而接受此信息是由exception接收,并调用toString()方法把exception转换成字符串,使用showStatus(exception.toString()); 显示出来 (因为需求要求显示异常)。b)、若当除数!=0时,那么就不抛出异常,然后将程序返回到try()程序块中quotient()方法调用处,接着使用showStatus()方法显示算式和结果(number1+”/”+number2+”=”+Double.toString(result))程序将跳过catch{}程序块,action()方法将执行return true
注重:当quotient()抛出异常信息(DivideByZeroException)时,quotient()终止执行,这样会对所有对象(4+2+1)设置标记,用于无用单元回收处理;并在无用单元回收处理之前,还将运行这些对象的终止函数。假如抛出异常,那么try在程序块执行showStatus之前终止。若try抛出异常之前自动创建了对象,那么会对这些对象设置标记,用于无用单元回收,同时在回收之前,还将运行这些对象的终止函数。
b、在action()方法中要注重处理:在TextField中第2个输入结束并按下回车后,除数应该怎样获得输入的数字(number1=Integer.parseInt(input1.getText)),文本框如何获得合法的文本(setText()).也包括判定(if (event.target==input2))
C、因为quotient()方法任务比较多,所以要将此方法单独与action来处理,而显示程序的清楚性和可读性,同时可以提高执行效率和稳定性能。
类图与流程图:
代码:
//Fig 16.4: DivideByZeroException.java
//W.Qiang
//2005.J.6
public class DivideByZeroException extends ArithmeticException{
public DivideByZeroException()
{
super(“Attempted to divide by zero”);
}
}
import java.awt.*;
import java.awt.event.*
import java.applet.Applet;
public class DivideByZeroTest extends Applet
{
Label prompt1,prompt2;
TextField input1,input2;
Int number1,number2;
Double result;
public void init()
{
prompt1=new Label (“输入被除数”);
input1=new TextField(10);
prompt2=new Label (“输入除数并按下回车”);
input2=new TextField(10);
add (prompt1);
add (input1);
add (prompt2);
add (input2);
}
public boolean action (Event event,Object obeject )
{
if (event.target==input2)
{
number1=Integer.parseInt(input1.getText());
input1.setText(“”);
number2=Integer.parseInt(input2.getText());
input2.setText(“”);
try {
result=quotient(number1,number2);
showStatus(number1+”/”+number2+”=”+Double.toString(result));
}
catch(DivideByZeroException exception) {
showStatus(exception.toString());
}
}
return true;
}
public double quotient(int numerator,int denominator)throws DivideByZeroException
{
if (denominator==0)
throw new DivideByZeroException ();
else (denominator!=0)
return(double) numerator / denominator ;
}
}
12.6 try程序块:
try{
……}
catch(){
…….}
finally{ ……}
try后面跟0~多个catch程序块
若try抛出Exception,App寻找在Catch1~100寻找合适异常处理程序,若找到,执行CATCH{}代码,没有,执行最后一个catch{}后代码
若try未抛出Exception,就执行执行最后一个catch{}后代码。
finally{无类是否出现异常都执行的程序,必须完成资源释放即终止函数调用,可避免资源泄露}
16.7 抛出异常:
throw 子句用来抛出异常,而throws子句用来指定异常
throw 的操作数是Throwable所有派生类,Throwable的直接子类是Exception(异常,由应捕捉的问题引起,应进行处理)与Error(重大系统问题,一般不捕捉)
抛出异常抛出点有try{}块、, try{}块某个深层嵌套的作用域、try{}块某个深层嵌套的方法中,throws指定异常,throw抛出的异常
try{}不包括错误检测语句与throw子句,但它的程序块中所引用的对象将会执行构造函数中的错误检测代码,并抛出异常
我们只要求异常终止产生异常的程序块执行,而不停止整个程序
异常信息传递通过对象引用产生,然后让catch块中的参数名(对象名)引用
16.8 捕捉异常:
异常处理程序包含在catch程序块中
语法:
catch (classNmae---指定要抛出的异常的类型,参数名-----用来引用处理程序捕捉的对象){javaCode -----处理异常可执行代码}
catch使用注重事项:
1、若假设异常处理之后,程序控制将返回throw后的第一个语句,那么将导致逻辑错误
2、将catch程序块的参数不能设置成多个,一个catch只有一个参数
3、若两个catch程序块(均和某个try程序块有关)都用于捕捉同一类型异常,那么将产生语法错误
4、捕捉非凡异常可能找不到,需要在下一层try中找,若找不到,那么命令行APP/控制台APP(不基于GUI)将退出程序,Applet或基于GUI的APP将继续执行处于一个不稳定的状态的APP
5、若某一类型异常,可能有几个异常处理程序与他相匹配,那么执行first相匹配的异常处理程序
6、一个程序可以同时处理许多关系密切的异常,我们可谓此ExceptionGroup提供1个异常类与catch处理程序,当某个异常发生时,可根据不同的实例数据创建异常对象,同时catch检查该数据,以判定异常的类型;我们一般不提倡此种编程风格,最好用继续的方法解决
7、在异常处理程序中,不能访问try块中定义的对象,异常处理开始,try快结束
8、若执行某个异常处理程序时又抛出一个异常,原try块已经终止,那么就让原try外层try处理程序进行处理,同时外层try程序监视并处理原try块的catch处理程序产生的错误
9、异常处理程序的形式:A、重抛出异常;B、通过抛出另一种不同类型的异常来转换异常类型;C、通过执行完最后一个异常处理程序之后,完成任何必要的恢复工作并使程序继续执行;D、判定产生错误的原因,然后处理,并重新调用原来曾产生该异常的方法 E、简单向Runtime返回一个状态值 ……………等等
10、传统的控制流不使用异常,因“额外的”异常会“掩盖”那些真正的错误类型的异常,程序员很难跟踪那些大量的异常,而且这里的异常不是经常见到的
11、catch处理程序抛出一个异常,将由catch处理或与同1个try块相关的其他处理程序来处理,就会产生逻辑错误
16.9 重抛出异常:
catch异常处理程序中出现异常,需要其他catch处理程序处理该异常
可以用throw重抛出异常
16.10 throws 子句:
用途:在一个method中,利用其列出该方法所能抛出的异常
int g (float h) throws a,b,c
{
//method body
}
抛出的对象为:指定类对象或者子类对象
1、运行时异常:发生异常是某些可避免的事情出现错误
此异常是RuntimeException类派生出来的
包括:ArrayIndexOutOfBoundsException(访问一个越界数组下标),NullPointerException(Null引用操作抛出---声明而没有创建对象);
ClassCastException (无效的类型转换引发)
IOException ,InterruptedException
Throws不必要显式指出RuntimeException与Error,而要指出method或该method所调用的method显式抛出非RuntimeException异常
3、java异常区分:经检查的Exception(非RuntimeException);未经检查的Exception(RuntimeException);Error
注重事项:
1、method抛出一个经检查的Exception,若该Exception未列在throws子句中,会产生语法错误
2、试图在一个没有throws子句中method抛出异常,会产生语法错误
3、若method1调用method2(显示抛出经检查的Exception),必须在method1的子句列出此异常,除非method2已经捕捉此异常
4、子类method的throws清单是父类method的throws清单的子集
5、捕捉异常必须处理,才能满足“java捕捉或声明”。
6、java编译器强制程序员处理异常,绝对不能忽略异常处理
7、java列出的Error见j2SDK 1.42 Documention 的java.lang package errors (AWTError)
8、java列出的Exception见j2SDK 1.42 Documention 的java.lang package Exception 、 java.util package Exception 、java.io package Exception、java.awt package Exception、java.net package Exception
16.11构造函数、终止函数和异常处理
当构造函数检测错误(不能返回值)解决方案(让程序知道无法正确构造该对象):
1、直接返回该错误对象,当使用该对象进行判定,以确定此对象是否错误
2、在该构造函数外设定一个全局变量,都不是好方法3、抛出异常,把次函数错误信息传递到外部,并处理该错误
4、当该函数抛出Exception ,那么该函数建立的对象将被设置标记,用与最终的无用单元回收处理,故需要调用终止函数。
16.12异常与继续:
因为继续关系,各种错误可以多态处理
一般最好捕捉并处理父类异常,(仅当所有子类对象的处理方法都一致才有意义),否则,捕捉子类异常
16.13 finally程序块
若程序显示使用某些资源,那么必须在最后完成对资源的释放即无庸单元回收,在C与C++中,常见是指针不能回收,函数不能终止,出现“内存泄露”
java实现自动的无庸的单元回收功能,可避免“内存泄露”,但java同样存在别的“资源泄露”
一般在finally程序块中使用一些资源释放的代码
1、finally程序块将try程序块中所有打开文件关闭
2、java并没有彻底消除资源泄露、当某个对象不存在是,java才对该对象进行无庸单元回收处理,当程序员错误的对某个对象保持引用时,就会出现内存泄露
3、exception ( String informationString):其中informationString是对该类异常描述信息,而获得信息使用getMessage(用于返回保存在某个异常中的描述字符串)。PrintStackTrace(用于把方法调用堆栈的内容打印出来,对测试程序非凡有用)。