异常机制已经成为判断一门编程语言是否成熟的标准,异常机制可使程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提升程序健壮性。 html
Java异常机制主要依赖于try、catch、finally、throw、throws五个关键字。 java
1.try:它里面放置可能引起异常的代码 数据库
2.catch:后面对应异常类型和一个代码块,用于代表该catch块用于处理这种类型的代码块,能够有多个catch块。 编程
3.finally:主要用于回收在try块里打开的物力资源(如数据库链接、网络链接和磁盘文件),异常机制老是保证finally块老是被执行。只有finally块,执行完成以后,才会回来执行try或者catch块中的return或者throw语句,若是finally中使用了return或者 throw等终止方法的语句,则就不会跳回执行,直接中止。 小程序
4.throw:用于抛出一个实际的异常,能够单独做为语句使用,抛出一个具体的异常对象。 网络
5.throws:用在方法签名中,用于声明该方法可能抛出的异常。 编程语言
Java的异常分为两种,checked异常(编译时异常)和Runtime异常(运行时异常) spa
1. java认为checked异常都是能够再编译阶段被处理的异常,因此它强制程序处理全部的checked异常,而Runtime异常无须处理,java程序必须显式处理checked异常,若是程序没有处理,则在编译时会发生错误,没法经过编译。 .net
2. checked异常体现了java设计哲学:没有完善处理的代码根本不会被执行,体现了java的严谨性, 设计
对于构造大型、健壮、可维护的应用系统而言,错误处理是整个应用须要考虑的重要方面。Java异常处理机制,在程序运行出现意外时,系统会生成一个Exception对象,来通知程序,从而实现将“业务功能实现代码”和“错误处理代码”分离,提供更好的可读性。
若是执行try块里的业务逻辑代码时出现异常,系统会自动生成一个异常对象,该异常对象被提交给运行环境,这个过程被称为抛出(throw)异常。Java环境收到异常对象时,会寻找合适的catch块,若是找不到,java运行环境就会终止,java程序将退出。
不一样的catch块,视为了针对不一样的异常类,提供不一样的处理方法。
对于错误处理机制,主要有以下的两个缺点:
1.没法穷举全部异常状况:由于人类的知识是有限的,异常状况总比能够考虑到的状况多,总有漏网之鱼
2.错误处理代码和业务实现代码混杂严重影响程序的可读性,会增长程序维护的难度。
1.使用try...catch捕获异常
java提出了一种假设,若是程序能够顺利完成,那么一切正常,把系统的业务实现代码放在try块中定义,全部的异常处理逻辑放在catch块中进行处理。
即:try{
//业务实现代码
...
}
catch(Exception e){
输入不合法
}
上面的格式中try块和catch块后的{...}都是不能够省略的!
执行步骤:
1.若是执行try块中的业务逻辑代码时出现异常,系统自动生成一个异常对象,该异常对象被提交给java运行环境,这个过程称为抛出(throw)异常。
2.当java运行环境收到异常对象时,会寻找能处理该异常对象的catch块,若是找到合适的cathc块并把该异常对象交给catch块处理,那这个过程称为捕获(catch)异常;若是java运行时环境找不到捕获异常的catch块,则运行时环境终止,jav程序也将退出。
注意1:无论程序代码块是否处于try块中,甚至包括catch块中代码,只要执行该代码时出现了异常,系统都会自动生成一个异常对象,若是程序没有为这段代码定义任何catch块,java运行环境确定找不处处理该异常的catch块,程序确定在此退出。
注意2:try块后能够有多个catch块,try块后使用多个catch块是为了针对不一样异常类提供的不一样的异常处理方式。当系统发生不一样意外状况时,系统会生成不一样的异常对象,java运行时就会根据该异常对象所属的异常类来决定使用哪一个catch块来处理该异常。
注意3:一般状况下,若是try块被执行一次,则try块后只有一个catch块会被执行,毫不可能有多个catch块被执行,除非在循环中使用类continue开始下一次循环,下一次循环又从新运行了try块,这才可能致使多个catch块被执行。
注意4:进行异常捕获时,必定要记住先捕获小的异常,再捕获大的异常。
Java的异常类,以及他们的继承关系:
java把全部非正常状况分红两种:异常(Exception)和错误(Error),都是继承自Throwable父类。
Error错误:通常是指虚拟机相关的问题,如系统崩溃,虚拟机出错误等,这种错误没法恢复或不可能捕获,将致使应用程序中断,一般不处理。
Throwable():Throwable 类是 Java 语言中全部错误或异常的超类。只有当对象是此类(或其子类之一)的实例时,才能经过 Java 虚拟机或者 Java throw 语句抛出。相似地,只有此类或其子类之一才能够是 catch 子句中的参数类型。
1.Error(错误):通常是指java虚拟机相关的问题,如系统崩溃、虚拟机出错误、动态连接失败等,这种错误没法恢复或不可能捕获,将致使应用程序中断,一般应用程序没法处理这些错误,所以应用程序不该该捕获Error对象,也无须在其throws子句中声明该方法抛出任何Error或其子类。
2.Exception:Exception 类及其子类是 Throwable 的一种形式,它指出了合理的应用程序想要捕获的条件
(1). SQLException:该异常提供关于数据库访问错误或其余错误的信息。
(2). RuntimeException 是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类
(3).IOException:此类为异常的通用类,它是由失败的或中断的 I/O 操做生成的。
异常对象包含的经常使用方法:
1. getMessage();返回该异常的详细描述字符
2. printStackTrace():将该异常的跟踪栈信息输出到标准错误输出。
3. printStackTrace(PrintStream s):将该异常的跟踪栈信息输出到指定的输出流
4. getStackTrace():返回该异常的跟踪栈信息。
public class TestException { public static void main(String[] args) { try{ FileInputStream fis=new FileInputStream("a.txt"); } catch(IOException ioe) { System.out.println(ioe.getMessage()); ioe.printStackTrace(); } } }
使用finally回收资源
有时候,程序在try块里面打开了一些物力资源(好比数据库链接,网络链接好磁盘文件等),这些物理资源都必须显式回收。
由于:java的垃圾回收机制不会回收任何的物理资源,垃圾回收机制只回收堆内存中对象所占用的内存。
问题1:那么在哪边回收这些物理资源呢?
答:在finally块中,由于若是try块的某条语句引发一场,该语句后的其余语句一般不会被执行,那将致使位于该语句后的资源回收语句得不到执行;若是在catch块里进行资源回收,但catch块彻底有可能得不到执行,这也将致使不能及时回收这些物理资源。因此咱们无论try块中的代码是否出现异常,也无论哪一个catch块会被执行,finally块总会被执行。
那么:java异常处理的完整语法结构以下:
try
{
//业务实现逻辑
...
}
catch(SubException e)
{
//异常处理快1
...
}
catch(SubException2 e)
{
//异常处理快2
...
}
...
finally
{
//资源回收块
...
}
以上的异常处理语法结构中
注意点1:只有try块石必须的,也就是说若是没有try块,则不可能有后面的catch块和finally块;
注意点2:catch块和finally块都是可选的,但catch块和finally块至少出现其中之一,也能够同时出现;
注意点3:能够有多个catch块,捕获父类异常的catch块必须位于捕获子类异常的后面;
注意点4:不能只有try块,既没有catch块,也没有finally块;
注意点5:多个catch块必须位于try块以后,finally块必须位于全部catch块以后。
import java.io.FileInputStream; import java.io.IOException; public class TestException { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub FileInputStream fis = null; try { fis = new FileInputStream("a.txt"); } catch (IOException ioe) { System.out.println(ioe.getMessage()); // return语句强制方法返回 return; // 使用exit来退出虚拟机 // System.exit(1); } finally { // 关闭磁盘文件,回收资源 if (fis != null) { try { fis.close(); } catch (IOException ioe) { ioe.printStackTrace(); } } System.out.println("程序已经执行了finally里德资源回收"); } } }
运行程序结果:
a.txt (系统找不到指定的文件。)
程序已经执行了finally里德资源回收
若是将catch块中的最后两句注释放入程序,那么结果为:a.txt (系统找不到指定的文件。)
以上两种状况显示:除非在try块或者catch块中调用了退出虚拟机的方法(即System.exit(1);),不然无论在try块、catch块中执行怎样的代码,出现怎样的状况,异常处理的finally块老是会被执行的。不过,通常状况下,不要再finally块中使用renturn或throw等致使方法终止的语句,由于一旦使用,将会致使try块、catch块中的return、throw语句失效。
public class TestException1 { public static boolean test() { try { return true; } finally { return false; } } public static void main(String[] args) { boolean a = test(); System.out.println(a); } }
运行结果:false
以上的小程序说明:在finally块中定义了一个renturn false语句,这将致使try块中的return true 失去做用!
总结一下这个小问题:
当程序执行try块,catch块时遇到return语句或者throw语句,这两个语句都会致使该方法当即结束,因此系统并不会当即执行这两个语句,而是去寻找该异常处理流程中的finally块,若是没有finally块,程序当即执行return语句或者throw语句,方法终止。若是有finally块,系统当即开始执行finally块,只有当finally块执行完成后,系统才会再次跳回来执行try块、catch块里的return或throw语句,若是finally块里也使用了return或throw等致使方法终止的语句,则finally块已经终止了方法,不用再跳回去执行try块、catch块里的任何代码了。
综上:尽可能避免在finally块里使用return或throw等致使方法终止的语句,不然可能出现一些很奇怪的状况!
异常处理的嵌套
例如catch块中再次包含了一个完整的异常处理流程,这种在try块,catch块或finally块中包含完整的异常处理流程的情形称为异常处理的嵌套。异常处理流程的代码能够放在任何可执行代码的地方,所以完整的异常处理流程既可放在try块,也可放在catch块,也可放在finally块里。
嵌套的深度没有很明确的限制,一般没有必要写层次太深的嵌套异常处理,会致使程序可读性下降。
Checked异常和Runtime异常体系
java异常被分为两大类:Checked异常和Runtime异常(运行时异常)。
全部RuntimeException类及其子类的实例被称为Runtime异常,不是RuntimeException类及其子类的异常实例则被称为Checked异常。
只有java语言提供了Checked异常,其余语言都没有提供,java认为Checked异常都是能够被处理(修复)的异常,因此java程序无须显式的处理Checked异常。若是程序没有处理Checked异常,该程序在编译时就会发生错误,没法经过编译。
Checked异常的处理方式:
①:当方法明确知道如何处理异常,程序应该使用try...catch块来捕获该异常,而后在对应的catch块中修补该异常。
②:当方法不知道如何处理异常,应该在定义该方法时声明抛出该异常。
Runtime异常无须显式声明抛出,若是程序须要捕捉Runtime异常,也可使用try...catch块来捕获Runtime异常。
问题是:大部分的方法老是不能明确知道如何处理异常,这就只能声明抛出异常了。
使用throws抛出异常
使用throws抛出异常的思路是:当前方法不知道如何处理这种类型的异常,该异常应该由上一级调用者处理,若是main方法也不知道应该如何处理这种类型的异常,也可使用使用throws声明抛出异常,该异常将交给JVM来处理。
JVM对异常的处理方法:打印异常跟踪栈的信息,并终止程序运行,因此有不少程序遇到异常后自动结束。
使用throws抛出异常的格式:
throws声明的抛出的语法格式紧跟在方法以后,能够声明多个异常类,多个异常类之间以逗号隔开。一旦使用了throws语句声明抛出异常,就不用再使用try...catch来捕获异常了。
如:throws ExceptionClass1,ExceptionClass2...
注意点1:若是某段代码调用了一个带throws声明的方法,该方法声明抛出了Checked异常,这代表该方法但愿它的调用者来处理该异常。那么这段代码要么放在try块中显示捕获该异常,要么这段代码处于另外一个带throws声明抛出的方法中。
举例以下:
//方法一: import java.io.FileInputStream; import java.io.IOException; public class TestException2 { // test() 方法抛出了异常,那么test()方法的调用者要么放在try块中显示捕获该异常,要么这段代码处于另外一个带throws声明抛出的方法中。 // 如下为后者的处理方法 public static void test() throws IOException { FileInputStream fis = new FileInputStream("a.txt"); } public static void main(String[] args) throws Exception { test(); } }
//方法二: import java.io.FileInputStream; import java.io.IOException; public class TestException2 { public static void test() throws IOException { FileInputStream fis = new FileInputStream("a.txt"); } public static void main(String[] args) { try { test(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
使用throws声明抛出异常时有一个限制:就是方法重写时的“两小”中的一条规则:子类方法声明抛出的异常类型应该是父类方法声明抛出的异常类型的子类或或相等,子类方法中不容许比父类方法声明抛出更多异常。即若是子类抛出的异常是父类抛出的异常的父类,那么程序没法经过编译。
由于Checked异常存在一些不便之处,大部分状况,可使用Runtime异常,若是程序须要在合适的地方捕获异常,并对异常进行处理,程序同样能够用try...catch捕获Runtime异常。
使用throw抛出异常
当程序出现错误时,系统会自动抛出异常,另外,java也容许程序自行抛出异常,自行抛出异常使用throw语句完成!
抛出异常:
若是须要在程序中自行抛出异常,应使用throw语句,throw语句能够单独使用,throw语句抛出的不是异常类,而是一个异常实例,并且每次只能抛出一个异常实例。throw语句的格式以下:throw ExceptionInstance;
throw语句抛出异常的两种状况:
1.当throw语句抛出的异常是Checked异常,则该throw语句要么处于try块里显式捕获该异常,要么放在一个带throws声明抛出的方法中,即把异常交给方法的调用者处理。
2.当throw语句抛出的异常是Runtime异常,则该语句无须放在try块内,也无须放在带throws声明抛出的方法中,程序既能够显式使用try...catch来捕获并处理该异常,也能够彻底不理会该异常,把该异常交给方法的调用者处理。
举例以下:
public class TestException3 { public static void throwChecked(int a) throws Exception { if (a < 0) { /** * 自行抛出Exception异常 改代码必须处于try块里,或处于带throws声明的方法中 */ throw new Exception("a的值大于0,不符合要求"); } } public static void throwRuntime(int a) { if (a < 0) { /** * 自行抛出RuntimeException异常,既能够显式捕获该异常 也能够彻底不用理会该异常,把该异常交给方法的调用者处理 */ throw new RuntimeException("a的值大于0,不符合要求"); } else { System.out.println("a的值为:" + a); } } public static void main(String[] args) { try { /** * 此处调用了带throws声明的方法,必须显示捕获该异常(使用try...catch) 不然,要在main方法中再次声明抛出 */ throwChecked(-3); } catch (Exception e) { System.out.println(e.getMessage()); } throwRuntime(3); } }
由上面的代码显式:自行抛出Runtime异常比自行抛出Checked异常的灵活性更好。
java的异常跟踪栈
异常对象的printStackTrace方法用于打印异常的跟踪栈信息,根据printStackTrace方法的输出结果,咱们能够找到异常的源头,并跟踪到异常一路触发的过程。
虽然printStackTrace()方法能够很方便地追踪异常的发生情况,能够用它来调试,可是在最后发布的程序中,应该避免使用它。而应该对捕获的异常进行适当的处理,而不是简单的将信息打印出来。
总之,要合理使用异常。