在程序运行过程当中,若是JVM检测出一个不可能执行的操做,就会出现运行时错误。java
在Java中,运行时错误会做为异常抛出。异常就是一个对象,表示阻止正常进行程序执行的错误或者状况。若是异常没有被处理,那么程序将会非正常终止。程序员
异常是从方法抛出的。方法的调用者能够捕获以及处理该异常。编程
throw语句的执行称为抛出一个异常。异常就是一个从异常类建立的对象。数组
当异常被抛出时,正常的执行流程就被中断。就像它的名字所提示的,“抛出异常”就是将异常从一个地方传递到另外一个地方。调用方法的语句包含在一个try块和一个catch块中。spa
throw语句相似于方法调用,但不一样于调用方法的是,它调用的是catch块。从某种意义上讲,catch块就像带参数的方法定义,这些参数匹配抛出的值的类型。可是,它不像方法,在执行完catch块以后,程序控制再也不返回到throw语句;而是执行catch块后的下一条语句。设计
一个异常多是经过try块中的throw语句直接抛出的,或者调用一个可能会抛出异常的方法而抛出。code
使用异常处理的优势:它能使方法抛出一个异常给它的调用者,并由调用者处理该异常。若是没有这个能力,那么被调用方法就必须本身处理异常或者终止该程序。被调用的方法一般不知道在出错的状况下该作什么,这是库方法的通常状况。库方法能够检测出错误,可是只有调用者才知道出现错误时须要作些什么。异常处理最根本的优点就是将检测错误(由被调用的方法完成)从处理错误(由调用方法完成)中分离出来。orm
异常时对象,而对象都采用类来定义。异常的根类是java.lang.Throwable。对象
Java API中有不少预约义的异常类。blog
Throwable类是全部异常类的根类。全部的Java异常类都直接或者间接地继承自Throwable。能够经过继承Exception或者Exception的子类来建立本身的异常类。
这些异常类能够分为三种主要类型:系统错误、异常和运行时异常。
系统错误是由Java虚拟机抛出的,用Error类表示。Error类描述的是内部系统错误。这样的错误不多发生。若是发生了,除了通知用户以及尽可能稳妥地终止程序外,几乎什么也不能作。
异常是用Exception类表示的,它描述的是由程序和外部环境所引发的错误,这些错误能被程序捕获和处理。ClassNotFoundException、IOException
运行时异常是用RuntimeException类表示的,它描述的是程序设计错误,例如,错误的类型转换、访问一个越界数组或数值错误。运行时异常一般是由Java虚拟机抛出的。ArithmeticException、NullPointerException、IndexOutOfBoundsException
RuntimeException、Error以及它们的子类都称为免检异常。全部其余异常都称为必检异常,意思是指编译器会强制程序员检查并经过try-catch块处理它们,或者在方法头进行声明。
在大多数状况下,免检异常都会反映出程序设计上不可恢复的逻辑错误。例如,若是经过一个引用变量访问一个对象以前并未将一个对象赋值给它,就会抛出NullPointerException;若是访问一个数组的越界元素,就会抛出IndexOutOfBoundsException异常,这些都是程序中必须纠正的逻辑错误。免检异常可能在程序的任何一个地方出现。为避免过多地使用try-catch块,Java语言不强制要求编写代码捕获或声明免检异常。
异常的处理器是经过从当前的方法开始,沿着方法调用链,按照异常的反向传播方向找到的。
Java的异常处理模型基于三种操做:声明一个异常、抛出一个异常和捕获一个异常。
声明异常
在Java中,当前执行的语句必属于某个方法。Java解释器调用main方法开始执行一个程序。每一个方法都必须声明它可能抛出的必检异常类型。这称为声明异常。由于任何代码均可能发生系统错误和运行时错误,所以,Java不要求在方法中显示声明Error和RuntimeException (免检异常)。可是,方法要抛出的其余异常都必须在方法头中显示声明,这样,方法的调用者会被告知有异常。
若是方法没有在父类中声明异常,那么就不能在子类中对其进行继承来声明异常。
抛出异常
检测到错误的程序能够建立一个适合的异常类型的实例并抛出它,这就称为抛出一个异常。
IllegalArgumentException ex = new IllegalArgumentException ("Wrong Argument");
throw ex;
或者,根据偏好,也可使用下面的语句:
throw new IllegalArgumentException (“Wrong Argument”);
声明异常的关键字是throws,抛出异常的关键字是throw。
捕获异常
若是在执行try块的过程当中没有出现异常,则跳过catch子句。
若是try块中的某条语句抛出一个异常,Java就会跳过try块中剩余的语句,而后开始查找处理这个异常的代码的过程。处理这个异常的代码称为异常处理器;能够从当前的方法开始,沿着方法调用链,按照异常的反向传播方向找到这个处理器。从第一个到最后一个逐个检查catch块,判断在catch块中的异常类实例是不是该异常对象的类型。若是是,就将该异常对象赋给所声明的变量,而后执行catch块中的代码。若是没有发现异常处理器,Java会退出这个方法,把异常传递给调用这个方法的方法,继续一样的过程来查找处理器。若是在调用的方法链中找不处处理器,程序就会终止而且在控制台上打印出错信息。寻找处理器的过程称为捕获一个异常。P393
从一个通用的父类能够派生出各类异常类。若是一个catch块能够捕获一个父类的异常对象,它就能捕获那个父类的全部子类的异常对象。
在catch块中异常被指定的顺序是很是重要的。若是父类的catch块出如今子类的catch以前,就会致使编译错误。
Java强迫程序员处理必检异常,若是方法声明了一个必检异常(即Error或RuntimeException以外的异常),就必须在try-catch中调用它,或者在调用方法中声明要抛出异常。
从异常中获取信息
异常对象包含关于异常的有价值的信息。能够利用下面这些java.lang.Throwable类中的实例方法获取有关异常的信息。printStackTrace()方法在控制台上打印栈跟踪信息。getStackTrace()方法提供编程的方式,来访问由printStackTrace()打印输出的栈跟踪信息。
在异常事件中,执行仍然会继续。若是处理器没有捕获到这个异常,程序就会忽然中断。
不管异常是否产生,finally子句老是会被执行。
有时候,不论异常是否出现或者是否被捕获,都但愿执行某些代码。Java有一个finally子句,能够用来达到这个目的。
在任何状况下,finally块中的代码都会执行,不论try块中是否出现异常或者是否被捕获。考虑下面三种可能的状况:
(1)若是try块中没有出现异常,执行finalStatements,而后执行try语句的下一条语句。
(2)若是try块中有一条语句引发异常,并被catch捕获,而后跳过try块的其余语句,执行catch块和finally子句。执行try语句以后的下一条语句。
(3)若是try块中有一条语句引发异常,可是没有被任何catch块捕获,就会跳过try块中的其余语句,执行finally子句,而且将异常传递给这个方法的调用者。
即便在到达finally块以前有一个return语句,finally块仍是会执行。
当错误须要被方法的调用者处理的时候,方法应该抛出一个异常。
try块包含正常状况下执行的代码。catch块包含异常状况下执行的代码。异常处理将处理错误的代码从正常的程序设计任务中分离出来,这样,可使程序更易读、更易修改。可是,应该注意,因为异常处理须要初始化新的异常对象,须要从调用栈返回,并且还须要沿着方法调用链来传播异常(这里注意方法调用的方向和异常传播的方向是相反的)以便找到它的异常处理器,因此,异常处理一般须要更多的时间和资源。
异常出如今方法中。若是想让该方法的调用者处理异常,应该建立一个异常对象并将其抛出。若是能在发生异常的方法中处理异常,无须抛出异常。
若是异常处理器不能处理一个异常,或者只是简单地但愿它的调用者注意到该异常,Java容许该异常的处理器从新抛出异常。
try{ statements; } catch(TheException ex) { perform operation before exits; throw ex; }
语句throw ex从新抛出异常给调用者,以便调用者的其余处理器得到处理异常ex的机会。
能够经过派生java.lang.Exception类来定义一个自定义异常类
若是遇到一个不能用预约义异常恰当描述的问题,那就能够经过派生Exception类或其子类,例如IOException,来建立本身的异常类。