java程序错误类型及异常处理
⼀、程序的错误类型
在程序设计中,⽆论规模是⼤是⼩,错误总是难免的。程序的设计很少有能够⼀次完成,没有错误的(不是指HelloWorld这样的程序,⽽是要实现⼀定的功能,具备⼀定实⽤价值的程序),在编程的过程中由于种种原因,总会出现这样或那样的错误,这些程序的错误就是我们常说的“Bug”,⽽检测并修正这些错误的⽅法就是“Debug”(调试)。
基本上所有的集成开发环境都提供了强⼤的和程序调试功能,在程序进⾏编译,连接,运⾏时,会对程序中错误进⾏诊断。
程序的错误可以抽象分为三类:语法错误、运⾏错误和逻辑错误。
1、语法错误
是指由于编程中输⼊不符合语法规则⽽产⽣的。程序编译就通不过,程序不能运⾏起来。此类错误最简单,调试起来⽐较容易
例如:表达式不完整、缺少必要的标点符号、关键字输⼊错误、数据类型不匹配、循环语句或选择语句的
关键字不匹配等。通常,编译器对程序进⾏编译的过程中,会把检测到的语法错误以提⽰的⽅式列举出来,⼜称为编译错误。
语法错误的调试,则可以由集成开发环境提供的调试功能来实现,在程序进⾏编译时,编译器会对程序中的语法错误进⾏诊断。
编译诊断的语法错误分为3中:致命错误、错误和警告。
(1)致命错误:这个错误⼤多是编译程序内部发⽣的错误,发⽣这类错误时,编译被迫中⽌,只能重新启动编译程序,但是这类错误很少发⽣,为了安全,编译前最好还是先保存程序。
(2)错误:这个错误通常是在编译时,语法不当所引起的。例如:括号不匹配,变量未声明等。产⽣这类错误时,编译程序会出现报错提⽰,我们根据提⽰对源程序进⾏修改即可。这类错误是出现最多的。
(3)警告:是指被编译程序怀疑有错,但是不确定,有时可强⾏通过。例如:没有加void声明的主函数没有返回值,double数据被转换为float类型等。这些警告中有些会导致错误,有些可以通过。
常规解决⽅法:此类错误⼀般程序编译系统会⾃动提⽰相应的错误地点和错误原因,⽐如哪⼀⾏代码少了个括号等诸如此类的提⽰,常见的错误,看懂直接改正即可,如果是看不懂原因,可以将错误提⽰信
nullpointerexception为什么异常息输⼊搜索引擎查⼀下,⼀般都能到具体的解决办法。或者有些编程平台会本⾝提供⼀个本地或者在线的信息库,提供详细的错误原因和解决办法,⽐如微软的.NET开发平台。
2、运⾏错误
指程序在运⾏过程中出现的错误。程序通过语法错误检测,但是运⾏的时候出现错误,导致程序被迫终⽌,此类错误有特定的发⽣条件,因此能够准确的定位错误代码段,因⽽调试也⽐较⽅便。
例如:除法运算时除数为0 、数组下标越界、⽂件打不开、磁盘空间不够、数据库连接错误等。
此类错误发⽣时,编译平台⼀般也会提⽰相应的信息,对于常规的错误会有⽐较精确地提⽰,但有时提⽰的错误原因会⽐较模糊,但因为此类错误⼀般在程序运⾏时,只在特定的条件下才会发⽣,所以根据错误发⽣的条件,能够⼤致判断程序出错的代码段,结合错误的原因,也能⽐较⽅便的调试出错误。
3、逻辑错误
程序运⾏后,没有得到设计者预期的结果,这就说明程序存在逻辑错误。这种错误在语法上是有效的,但是在逻辑上是错误的。
程序运⾏了,也没有出错,但是执⾏出来的结果不是⽤户想要的,分为两种情况:
A、能够看出错误:⽐如查询⼯资⼤于5000的⼈员名单,却出现了3000的;
B、看不出错误,直到因缘际会发现程序肯定出错了,后果很严重:⽐如进⾏⼀个符合⼤型运算,把某个常数输⼊错了,最后的结果⼈⼯⽆法判断对错,⼜以该结果进⾏其它的运算等等,最后发现错了误差过⼤,就得从头排查错误。
例如:使⽤了不正确的变量,指令的次序错误,循环的条件不正确,程序设计的算法考虑不周全等。通常,逻辑错误也会附带产⽣运⾏错误。在⼀般情况下,编译器在编译程序时,不能检测到程序中的逻辑错误,也不会产⽣逻辑错误的提⽰,因此逻辑错误⽐较难排除,需要程序员仔细的分析程序,并借助集成开发环境提供的调试⼯具,才能到出错的原因,并排除错误。
⼆、java的异常处理(错误处理)
程序的错误就是通常的异常,也叫Exception。
对于语法错误,java编译系统在编就能发现检查出错误。
对于逻辑错误,编译系统是⽆法发现错误的,错误需要⼈为去发现排除错误。
对于运⾏错误,Java语⾔中代表异常时,使⽤⼀个专门的类来代表⼀种特定的异常情况,在系统中传递的异常情况就是该类的对象,所有代表异常的类组成的体系就是Java语⾔中的异常类体系。
Java的异常是⼀个对象,所有的异常都直接或间接地继承Throwable类。Throwable类的继承层次结构如下:
Java异常层次结构图如下图所⽰:
为了⽅便对于这些可传递对象的管理,Java API中专门设计了java.lang.Throwable类,只有该类⼦类的对象才可以在系统的异常传递体系中进⾏。该类的两个⼦类分别是:
1)Error类
该类代表错误,指程序⽆法恢复的异常情况。对于所有错误类型以及其⼦类,都不要求程序进⾏处理。常见的Error类例如内存溢出StackOverflowError等。
2)Exception类
该类代表异常,指程序有可能恢复的异常情况。该类就是整个Java语⾔异常类体系中的⽗类。使⽤该类,可以代表所有异常的情况。
在Java API中,声明了⼏百个Exception的⼦类分别来代表各种各样的常见异常情况,这些类根据需要代表的情况位于不同的包中,这些类的类名均以 Exception作为类名的后缀。如果遇到的异常情况,Java API中没有对应的异常类进⾏代表,也可以声明新的异常类来代表特定的情况。
在这些异常类中,根据是否是程序⾃⾝导致的异常,将所有的异常类分为两种:
a)    RuntimeException及其所有⼦类
该类异常属于程序运⾏时异常,也就是由于程序⾃⾝的问题导致产⽣的异常,例如数组下标越界异常ArrayIndexOutOfBoundsException等。
该类异常在语法上不强制程序员必须处理,即使不处理这样的异常也不会出现语法错误。
b)    其它Exception⼦类
该类异常属于程序外部的问题引起的异常,也就是由于程序运⾏时某些外部问题导致产⽣的异常,例如⽂件不存在异常FileNotFoundException等。
该类异常在语法上强制程序员必须进⾏处理,如果不进⾏处理则会出现语法错误。
熟悉异常类的分类,将有助于后续语法中的处理,也使得在使⽤异常类时可以选择恰当的异常类类型。
异常类名⽤途
LinkageError动态链接失败
VirtualMachineError虚拟机错误
AWTError AWT错误
3、常见运⾏时异常类
异常类名⽤途
ArithmeticException数学运算异常,⽐如除数为零的异常
IndexOutOfBoundsException下标越界异常,⽐如集合、数组等
ArrayIndexOutOfBoundsException访问数组元素的下标越界异常
StringIndexOutOfBoundsException字符串下标越界异常
ClassCaseException类强制转换异常
NullpointerException当程序试图访问⼀个空数组中的元素,或访问⼀个空对象中的⽅法或变量时产⽣的异常。
4、常⽤的⾮运⾏时异常
异常类名⽤途
ClassNotFoundException指定类或接⼝不存在的异常
IllegalAccessException⾮法访问异常
Ioexception输⼊输出异常
FileNotFoundException不到指定⽂件的异常
ProtocolException⽹络协议异常
SocketException Socket操作异常
MalformedURLException统⼀资源定位符(URL)的格式不正确的异常
5、Java的异常处理机制描述如下:
在⼀个⽅法的运⾏过程中,如果发⽣了异常,
则这个⽅法(或者是Java虚拟机)⽣成⼀个代表该异常的对象(它包含了异常的详细信息),并把它交给运⾏时系统,运⾏时系统寻相应的代码来处理这⼀异常。我们把⽣成异常对象并把它提交给运⾏时系统的过程称为抛出(throw)⼀个异常。
运⾏时系统寻相应的代码来处理这⼀异常,系统在⽅法的调⽤栈中查,从产⽣异常的⽅法开始进⾏
回朔,沿着被调⽤的顺序往前寻,直到到包含相应异常处理的⽅法为⽌。其过程如图10-1所⽰。这⼀过程称为捕获(catch)⼀个异常。
如该异常未进⾏成功捕获,则程序将终⽌运⾏。
5、异常捕获和处理
格式:
try{
正常程序段,可能抛出异常;
}
catch (异常类1  异常变量) {
捕捉异常类1有关的处理程序段;
}
catch (异常类2  异常变量) {
捕捉异常类2有关的处理程序段;
}
……
finally{
⼀定会运⾏的程序代码;
}
l  try块——捕获异常:⽤于监控可能发⽣异常的程序代码块是否发⽣异常,如果发⽣异常,Try代码块将抛出异常类所产⽣的对象并⽴刻结束执⾏,⽽转向异常处理catch部分。
对于系统产⽣的异常或程序块中未⽤try监控所产⽣的⼀场,将⼀律由java 编译系统⾃动将异常对象抛出。
l  catch块——处理异常:抛出的异常对象如果属于catch内所定义的异常类,则catch会捕获该异常,并进⼊catch中的对应代码段继续运⾏程序,如果异常对象不属于catch中所定义的异常类,则进⼊finally块继续运⾏程序。
Catch包括两个参数:⼀个是类名,指出捕获的异常类型,必须使Throwable类的⼦类;⼀个是参数名,⽤来引⽤被捕获的对象。Catch块所捕获的对象并不需要与它的参数类型精确匹配,它可以捕获参数中指出的异常类的对象及其所有⼦类的对象
l  finally块——最终处理:⽆论是否发⽣异常都会执⾏的语句块。⽐如执⾏关闭打开的⽂件、删除临时⽂件,关闭数据库连接等操作。
注意:
l  ⼀个try、catch、finally块之间不能插⼊任何其它代码
l  catch可以有多个,try和finally只能有⼀个
l  try后⾯必须要跟catch、finally其中的⼀个,即但⼀个try、catch、finally语句只能省略catch、finally中的⼀个。
定义多个catch可精确地定位java异常。如果为⼦类的异常定义了特殊的catch块,⽽⽗类的异常则放在另外⼀个catch块中,此时,必须满⾜以下规则:⼦类异常的处理块必须在⽗类异常处理块的前⾯,否则会发⽣编译错误。所以,越特殊的异常越在前⾯处理,越普遍的异常越在后⾯处理。这类似于制订防⽕墙的规则次序:较特殊的规则在前,较普通的规则在后。
异常类常⽤⽅法
常⽤⾮法⽤途
Void String getMessage()返回异常对象的⼀个简短描述
Void String toString()获取异常对象的详细信息
Void printStackTrace()在控制台上打印异常对象和它的追踪信息
6、异常实例
1)        数学运算异常
class MathException{
public static void main(String args[]){
inta=5,b=0;
intc=a/b;        //除数为0,出现异常
System.out.print(c);
}
}
在命令提⽰符下运⾏该程序,可以发现编译正常,但是执⾏时出现错误的提⽰如下:
Exception inthread "main" java.lang.ArithmeticException: / by zero
at MathException.main(MathException.java:4)
翻译过来就是:
在类java.lang.ArithmeticException主线程中“main”⽅法中出现异常:除数为零,(MathException.java:4“此处指MathException类中的第四⾏”)
这是⼀个典型的运⾏错误,程序告诉了⼀下⼏个信息;
l  出错的异常类:java.lang.ArithmeticException
l  出错的类:MathException
l  出错的代码:MathException.java:4
因为编译系统给出了出错的原因和出错类的位置,可以⽅便地进⾏代码调试。
2)捕获数学运算异常的处理
public classTryCatchDemo{
public static void main(String args[]){
try {
int a=8,b=0;
int c=a/b;
System.out.print(c);
}
// ArithmeticException是异常类的名称,e是引⽤的参数名称
catch(ArithmeticException e)  {
System.out.println("发⽣的异常简短描述是:"+e.getMessage());
System.out.println("发⽣的异常详细信息是:"+e.toString());
}
}
}
程序执⾏结果:
发⽣的异常简短描述是:/ by zero
发⽣的异常详细信息是:java.lang.ArithmeticException: / by zero
3)数组下标越界异常
public class arrayException{
publicstatic void main(String[] args) {
//被监视的代码块
try{
int[]a=new int[4];
a[4]=9;
}
//处理下标越界异常
catch(ArrayIndexOutOfBoundsExceptionaiobe) {
System.out.println("这⾥出现的错误类型是:数组下标越界!!");
}
//处理空引⽤异常
catch(NullPointerExceptionnpe) {
System.out.println("这⾥出现的错误类型是:空引⽤!!");
}
finally {
System.out.println("程序⽆条件执⾏该语句!");
}
}
}
三、异常的抛出
异常的抛出可以分为两⼤类:
l  系统⾃动抛出异常
⽐如上⾯的例⼦就是系统⾃动抛出异常,通过try catch捕获异常对象,并继续相应的处理。
l  通过关键字throw将异常对象显性地抛出。