异常机制已经成为判断一门编程语言是否成熟的标准,例如Java,C#等。异常机制可以使程序中的异常处理代码和正常业务代码分离,使程序的逻辑更加清晰,并可以增强程序的容错性。
当一个程序出现错误时,它可能的情况有三种:
(1) 语法错误:指代码的格式错误,或者某个字母输入错误
(2) 运行时错误:指在程序运行时出现的一些没有想到的错误,如:空指针异常、数组越界、除数为零等
(3) 逻辑错误:指运行结果和预想的结果不一样,这是一种很难调试的错误。
在Java中的异常处理机制主要是指处理运行时错误,即异常就是运行时错误
产生异常的原因有以下3种:
(1) Java 内部发生错误,Java虚拟机产生异常
(2) 编写程序时由于错误引起的异常,如空指针异常、数组越界
(3) 通过 Throw 语句生成的异常。这种异常通常被称为 “检查异常” ,用来告知方法调用者的相关信息
Java 通过面向对象的方法处理异常:
在一个方法的运行过程中如果产生了异常,这个方法就会产生代表该异常的一个对象,把它交给运行时系统,运行时系统寻找相应的代码来处理这一异常。
其中,生成异常单对象并把它交给运行时系统的过程称为抛出( Throw )
运行时系统在方法的调用栈中查找,直到找到能处理异常的对象的过程称为捕获( catch )
Java 的异常机制主要依赖于 5 个关键字,其中:
(1) try :关键字后紧跟一个大括号括起来的代码块,简称 try 块,它里面放置的可能是引发异常的代码
(2) catch :catch 后对应异常类型和一个代码块,用于表明该 catch 块用于处理这种类型的代码块
(3) finally : 多个 catch 块后还可以跟一个 finally 块,finally 块用于回收到 try 块里打开的物理资源,异常处理机制会保证 finally 块总被执行
(4) throw : 用于抛出一个实际的异常,throw 可以单独作为语句使用,抛出一个具体的异常对象
(5) throws : 主要在方法签名中使用,用于声明该方法可能抛出的异常
Java 异常结构中定义有 Throwable 类,Exception 和 Error 是其派生的两个子类
其中,Exception (异常)表示由于网络故障、文件损坏、设备错误、用户名输入法等情况导致的异常
Exception(异常) 可分为执行异常(Runtime Exception) 和检查异常(Checked Exception)两种
Error 表示 Java 运行时环境出现的错误
如下图所示:
Throwable
|--Error //是系统不可恢复的错误,JVM发生错误
|--OutOfMemoryError //堆内存溢出
|--StackOverflowError //栈内存溢出
|--Exception //程序可检查处理的异常,常见的异常继承根
|--Java.text.ParseException //format解析对象时候发生
| 如:Date d = dateformat.parse("2015-5-5");
|--RuntimeException //非检查异常,javac忽略对这类异常的检查,执行异常
|--IllegaArgumentException
|--NullPointerException
|--ArrayIndexOutBoundsException
|--ClassCastException
|--NumberFormatException
关于异常的分类:
(1) Error 是 JVM 中出现的不可恢复的错误
**(2) ** Exception 是类(class)发生的异常
检查异常是在编译期发生;
非检查异常(Runtime Exception 执行异常)实在程序运行时发生
常见的几种Runtime Exception
非检查异常( 执行异常 )
检查异常
异常的处理主要包括捕获异常、程序流程的跳转和异常处理语句块的定义等…
当一个异常被抛出时,应该有专门的语句块来捕获这个被抛出的异常对象,这个过程叫做捕获异常
当一个异常类的对象被捕获后,用户程序就会发生流程的跳转,系统中止当前的流程而跳转至专门的异常处理语句块,或直接跳出当前程序或 JVM 。
当程序中抛出一个异常后,程序从导致异常的代码处跳出,JVM 检查寻找和 try 关键字匹配的处理该异常的 catch 块,如果找到,就将控制权交到 catch 块中的代码,然后往下执行程序,try 块中发生异常的代码不会被重新执行。
如果找不到处理该异常的 catch 块,在所有的 finally 块代码被执行和当前线程所属的 ThreadGroup 和 uncaughtException 方法被调用后,遇到异常的当前线程被中止。
异常处理的目的并不是为了避免发生异常,而是在异常发生时避免程序的异常终止,设法将损失降低到最小
Java 的异常处理是通过5个关键字来实现的:try、catch、throw、throws和finally。一般情况下使用 try 来执行一段程序,如果出现一个异常,系统会抛出( throw )一个异常,这时候可以通过它的类型来捕获( catch ) ,最后( finally )由默认处理器来处理
在 Java 程序里,异常对象是依靠 try/catch 语句来捕获和处理的。try/catch 异常处理语句分为 try 语句块和 catch 语句块,其代码格式如下:
try{
//try语句块,可能产生异常的多个语句
}catch{
//catch语句块,对异常进行处理
}
一般将可能产生异常情况的语句放入 try 语句块中,这个 try 语句块用来启动 Java 的异常处理机制,凡是可能抛出异常的语句,包括 Throw 语句和可能抛出异常的方法的调用语句,都应该包含在这个 try 语句块中。然后在 catch 语句块对异常进行处理。
Java 语言还规定,每个 catch 语句块都应该与一个 try 语句块相对应(catch不能单独使用)
例如(捕获空指针的异常,并显示信息):
public class TryCatch {
public static void main(String[] args) {
String str = null;
try {
int in = str.length();
System.out.println("正常执行");
}catch(Exception e){
//输出执行堆栈信息
e.printStackTrace();
System.out.println("出现异常");
}
}
}
/*运行结果:
java.lang.NullPointerException
at day01.TryCatch.main(TryCatch.java:7)
出现异常
*/
try 语句块中调用了可能抛出 Exception 的对象,catch 语句块则专门用来捕获这类异常。可见,catch 语句块应紧跟在 try 语句块后面。当 try 语句块中的某条语句在执行时产生了一个异常,此时被启动的异常处理机制会自动捕获它,然后流程自动跳过异常引发点后面的所有尚未执行的语句,而转至 try 语句块后面的 catch 语句块执行 catch 语句块中的语句
每个 try 语句块可以伴随一个或多个 catch 语句,用于处理可能产生的不同类型的异常,catch 捕获的异常类型由上而下地捕获异常类型的顺序应是子类到父类的。
在捕获时要注意,子类型异常在前,父类型异常在后,按照这样的顺序依次捕获,否则编译不通过
如下所示:
try {
//--
}catch(NullpointerException){
}catch(RuntimeException e){
}catch(Exception e){
}
finally 语句块中的语句是一种强制的、无条件执行的语句,即无论在程序中是否出现异常,也无论出现哪种异常,都必须执行 finally 语句块中所包含的语句
通常在 finally 语句块中可以进行资源的释放工作,如关闭打开的文件夹、删除临时文件等
finally 语句块紧接着 try-catch 结构中的最后一个 catch 语句块
代码如下:
try {
//--
}catch(NullpointerException){
}catch(RuntimeException e){
}catch(Exception e){
}finally{
}
在出现和未出现异常的情况下都要执行的代码可以放到 finally 语句块中。加入了 finally 语句块后有以下3种情况:
示例:带 finally 语句块的程序示例
public class TryCatchFinally {
public static void main(String[] args) {
try {
int i = 0;
int j = 20;
int a = j/i;
System.out.println("j/i输出的值是:"+a);
}catch(Exception e){
//输出执行堆栈信息
e.printStackTrace();
System.out.println("输出的异常是:"+e);
}finally {
System.out.println("执行到finally块内!!!!");
//嵌套try-catch-finally
try {
String name = null;
if(name.equals("王五")) {
System.out.println("你的名字是王五");
}
}catch(Exception e) {
System.out.println("又捕获到了一个异常");
}finally {
System.out.println("执行到内套的finally层中");
}
}
}
}
/*运算结果:
输出的异常是:java.lang.ArithmeticException: / by zero
执行到finally块内!!!!
又捕获到了一个异常java.lang.NullPointerException
执行到内套的finally层中
*/
在 Java 语句中 Try-Catch-Finally 语句允许嵌套。
同时,finally 语句块还可以和 break、continue以及 return 等控制流程语句一起使用。当 try 语句块中出现了上述语句时,程序必须先执行 finally 语句块,才能最终离开 try 语句块
代码如下:
while(true) {
try {
System.out.println("即将被break终止,退出循环");
break;
}finally{
System.out.println("但是finally块总要被执行到");
}
}
/*运算结果:
即将被break终止,退出循环
但是finally块总要被执行到
*/
语句块中出现了上述语句时,程序必须先执行 finally 语句块,才能最终离开 try 语句块
代码如下:
while(true) {
try {
System.out.println("即将被break终止,退出循环");
break;
}finally{
System.out.println("但是finally块总要被执行到");
}
}
/*运算结果:
即将被break终止,退出循环
但是finally块总要被执行到
*/