是人都会有犯错的时候,当程序员出现编程错误的时候就产生了异常。
编程错误分三类:编译错误;逻辑错误;运行时错误。
编译错误:
原因:没有遵循语言的规则 解决方法:由编译程序检查发现
逻辑错误:
原因:程序没有按照预期的方案执行
解决方法:利用调试技巧可以解决此类错误
运行时错误:
原因:在程序运行过程中,出现了不可能执行的操作
解决方法:异常处理
下面主要介绍下运行时错误和解决方法(异常处理)。
运行时错误(runtime error):程序运行时的一些不正常现象
常见的错误(Throwable):除数为0、数组下标越界、文件不存在、内存不够用等等。
这些错误可以分为致命性错误和非致命性的异常
致命性的错误(Error):程序进入了死循环、递归无法结束、内存溢出等。
这类错误只能在编程阶段解决,运行时程序本身无法解决。只能依靠其他程序干预,否则会一直处于非正常状态。
非致命性的异常(Exception):除数为0、操作数超出数据范围、文件并不存在、欲装入的类文件丢失、网络连接中断……这类错误是可以检测和处理的,所以产生了相应的异常处理机制。
——java中预定义了很多异常类,每个异常类代表一种运行错误。
一些常见的(Exception):
NullPointerException 空指针异常
ArrayIndexOutOfBoundsException 数组越界访问异常
ClassNotFoundException 试图访问一个根本不存在的类
IOException 输入/输出异常
NumberFormatException 错误的类型转换异常
ArrayStoreException 当向数组存入错误类型数据时
ArithmeticException 除数为0的算术异常
Java提供了异常处理机制,通过面向对象的方法来处理异常。
·在程序运行的过程中,如果发生了异常,则该程序(或Java虚拟机)生成一个代表该异常类的对象(包含该异常的详细信息),并把交给运行时系统;
·运行时系统从生成对象的代码开始,沿方法的调用栈逐层回溯查找,直到找到包含相应处理代码的方法,并把异常对象交给该方法,来处理这一异常。
异常处理的机制:使用try-catch-finally语句捕获和处理异常
1、try语句块
·将程序中可能出现异常的代码放入try块中。
·当try块中有语句引发异常时,系统将不再执行try块中未执行的语句,而执行匹配的catch块。
·如果try块中没有语句引发异常,则程序执行完try块中的语句后不执行catch块中的语句,
即跳过catch语句,继续执行后面的程序。
2、catch块
·每个try语句后面必须伴随一个或多个catch语句,用于捕捉try语句块所产生的异常并作相应的处理。
·catch子句的目标是解决“异常”情况,并像没有出错一样继续运行。
try例可以有多个catch块,当使用多个catch块时,需注意catch子句排列
顺序--先特殊到一般,也就是子类在父类前面。如果子类在父类后面,子类将永远不
会到达。 try和它的catch是一个单元,catch不能捕获其他try声明所引发的异常。
try不能单独使用。
一个异常对象能否被一个catch块接收主要看该异常对象与catch块中声明的
异常类的匹配情况,当它们满足下面条件中的任一条时,异常对象将被接受:
异常对象是catch块中声明的异常类的实例;
异常对象是catch块中声明的异常类的子类的实例;
异常对象实现了catch块中声明的异常类的接口。
finally语句块
某些情况下,不管异常是否发生,都需要处理某些语句,那么就将这些语句
放到finally语句块中。finally语句所包含的代码在任何情况下都会被执行。
一个try语句至少有一个catch语句或finally语句与之匹配,但匹配的
catch可以有多个,而finally语句只能有一个,并且finally语句并非必须有的。
格式:
try{ //产生异常的语句 }
catch(异常类1 变量)
{
//异常处理代码
}
catch(异常类2 变量)
{
//异常处理代码
}
[finally{ }]
异常是类所以根据需要可以人工创建并抛出(Throwable)
如果一个方法中的代码在运行时可能生成某种异常,但是在本方法中不必要,或者不能确定如何处理此类异常时,则可以使用throws声明抛弃异常;
表明该方法中将不对此类异常进行处理,而由该方法的调用者负责处理;
即系统将在调用该方法的上层方法体内寻找合适的异常处理代码,而不再继续执行该方法的正常处理流程。
语法格式:throw 异常类对象;
例:IOException e = new IOException(); throw e;
程序执行throw语句后立即终止,然后在包含它的所有try块中从里向外寻找
含有与其类型匹配的catch子句。
声明抛弃异常的格式
类型 方法名([参数表]) throws 异常类型,…
{ //方法体; }
自定义异常类:虽然Java的内置异常处理能够处理大多数常见错误,但用户仍需建立
自己的异常类型来处理特殊情况。这时可以通过创建Exception的子类来定义自己的
异常类。
Exception类自己没有定义任何方法。但它继承了Throwable提供的一些方法。
格式:class 类名 extends Exception
{ … … }
在Java中,对异常进行处理需要考虑以下因素:
如果异常事件是在运行时产生的,并且在JDK API中没有与该异常事件相对应的异常对象,则应创建用户自定义类型的异常对象。
如果可以预测异常对象的类型并且该异常是在程序运行时发生的,则建议应用JDK API中定义的系统异常类型,并且可以抛出这种类型的异常对象由JVM处理。
如果不能确定异常对象的类型以及异常发生的时机,则应该采用系统类型异常对象并由JVM处理。
对应用程序设计失误导致的数组越界、非法变量等类型的异常,如果要全部捕获所有类型的异常对象,会增加系统开销,导致程序的运行效率降低,建议应用程序可以不对此类异常进行捕获,而交由JVM进行处理。
对于实现输入/输出处理、网络通讯和数据库访问功能的代码,必须进行异常对象的捕获和处理 。
从JDK1.4版本开始,Java语言引入了断言(assert)机制。
目的:程序调试
测试代码或者调试程序时,总会做出一些假设,断言就是用于在代码中捕捉
这些假设 。
表现形式:断言就是程序中的一条语句,它对一个boolean表达式进行检查
一个正确程序必须保证这个boolean表达式的值为true;
如果该值为false,说明程序已经处于不正确的状态,系统给出警告或退出
如果没有断言机制,Java程序通常使用if-else或switch语句进行变量状态检查。
缺点:
由于检查的数据类型不完全相同,这样的语句形式不会统一。
因为检查仅仅是应用在测试阶段,而if-else或switch语句在发布以后仍然将起作用,
如果消除这些代码就意味着要注释或者删除这些代码,如果这些代码量很大就意味着工作很繁重并存在风险。
优点:
Java程序员用统一的方式处理状态检查问题;
断言只需在发行的时候关闭该功能即可。
在默认情况下断言是关闭的,因此在使用断言以前,
需要先开启断言功能,方法:
java –ea MyClass 或者
java –enableassertions MyClass 关闭断言功能的方法:
java –da MyClass 或者
java –disableassertions MyClass注意:断言检查通常在开发和测试时开启。为
了提高性能,在软件发布后,断言检查通常是关闭的。
语法格式为:assert Expression1
执行到assert语句时,如果Expression1的值为true,则程序正常执行,如果值为false,该语句创建一个Assertion Error对象,并抛出该对象。
格式:assert Expression1:Expression2当断言失败时,系统会自动将Expression2的值传递给新建的AssertionError对象,进而将其转换为一个消息字符串保存起来,可以获得更多、更有针对性的检查失败细节信息。
那什么时候用断言呢:
通常来说,断言用于检查一些关键的值,并且这些值对整个程序,或者局部功能的完成有很大的影响。
断言表达式应该短小、易懂,如果需要评估复杂的表达式,应该使用函数计
算。
使用断言的情况
·检查控制流:在if-else和switch语句中,可以在不应该发生的控制支流上加上
assert语句。如果这种情况发生了,assert能够检查出来。
·在私有方法计算前,检查输入参数是否有效,对于一些private的方法,要求输入满
足一些特定的条件,可以在方法开头使用assert进行参数检查;
·对于公共方法,通常不使用断言检查
在方法计算后,检查方法结果是否有效
·检查程序不变量