1.概念
在现实生活中,常常会遇到很多的问题;类似的,在java中也会出现种种的不正常情况,将这些不正常情况封装成为对象,就叫做异常。简单说:异常就是问题在java中的体现。
就像人生病了,病可以分为严重性疾病,例如:癌症;也可以分为非严重性疾病,例如:感冒,上火。类似的,在java中的异常体系(Throwable)可以分为严重性的异常(Error)和非严重性的异常(Exception)。无论Error还是Exception都具有一些共性,比如:不正常原因,引发原因;那么根据共性抽取原则,java就把这些共性抽取到父类Throwable中。
2. 异常的体系结构
从图中可以看出所有异常类型都是内置类 Throwable 的子类,因而 Throwable 在异常类的层次结构的顶层。Throwable 分成了两个不同的分支,一个分支是Error,它表示不希望被程序捕获或者是程序无法处理的错误。另一个分支是Exception,它表示用户程序可能捕捉的异常情况或者说是程序可以处理的异常。其中异常类 Exception 又分为运行时异常(RuntimeException )和非运行时异常。Java异常又可以分为不受检查异常( Unchecked Exception )和检查异常(Checked Exception )
3.异常之间的区别与联系
- Error
Error 类对象由 Java 虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关。
比如说:
Java虚拟机运行错误(Virtual MachineError) ,当JVM不再有继续执行操作所需的内存资源时,将出现OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止;还有发生在虚拟机试图执行应用时,如类定义错误(NoClassDefFoundError) 、链接错误(LinkageError)。这些错误是不可查的,因为它们在应用程序的控制和处理能力之 外,而且绝大多数是程序运行时不允许出现的状况。
对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。在Java中,错误通常是使用 Error 的子类描述。
- Exception
在 Exception 分支中有一个重要的子类RuntimeException(LinkageError) ,该类型的异常自动为你所编写的程序定义 ArrayIndexOutOfBoundsException(数组下标越界)、NullPointerException(空指针异常)、ArithmeticException(算术异常)、 MissingResourceException(丢失资源)、ClassNotFoundException(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。
这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生;而RuntimeException 之外的异常我们统称为非运行时异常,类型上属于 Exception 类及其子类,从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException 、 SQLException 等以及用户自定义的 Exception 异常,一般情况下不自定义检查异常。
注意 :Error
通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程;Exception
通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。
4.Java异常处理机制
java异常处理本质:抛出异常和捕获异常
- 抛出异常
要理解抛出异常,首先要明白什么是异常情形(exception condition),它是指阻止当前方法或作用域继续执行的问题。其次把异常情形和普通问题相区分,普通问题是指在当前环境下能得到足够的信息,总能处理这个错误。
对于异常情形,已经无法继续下去了,因为在当前环境下无法获得必要的信息来解决问题,你所能做的就是从当前环境中跳出,并把问题提交给上一级环境,这就是抛出异常时所发生的事情。抛出异常后,会有几件事随之发生。
首先,是像创建普通的java对象一样将使用 new 在堆上创建一个异常对象;然后,当前的执行路径(已经无法继续下去了)被终止,并且从当前环境中弹出对异常对象的引用。此时,异常处理机制接管程序,并开始寻找一个恰当的地方继续执行程序,这个恰当的地方就是异常处理程序或者异常处理器,它的任务是将程序从错误状态中恢复,使程序要换一种方式运行,要么继续运行下去。
- 捕获异常
在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(
exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。
当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适的异常处理器。运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。
注意:
对于运行时异常、错误和检查异常,Java技术所要求的异常处理方式有所不同。由于运行时异常及其子类的不可查性,为了更合理、更容易地实现应用程序,Java规定,运行时异常将由Java运行时系统自动抛出,允许应用程序忽略运行时异常。对于方法运行中可能出现的Error,当运行方法不欲捕捉时,Java允许该方法不做任何抛出声明。因为,大多数Error异常属于永远不能被允许发生的状况,也属于合理的应用程序不该捕捉的异常。
对于所有的检查异常,Java规定:一个方法必须捕捉,或者声明抛出方法之外。也就是说,当一个方法选择不捕捉检查异常时,它必须声明将抛出异常。
5.异常处理
- try-catch
语法:
try { // 程序代码 }catch(ExceptionName e1) { //Catch 块 }
例子:
public class Test1 {
public static void main(String[] args) {
int a=12;
int b=0;
int c=0;
try {
c=a/b;
}catch (ArithmeticException e){
e.printStackTrace();
System.out.println("异常为"+e);
}
System.out.println(c);
}
}
异常两种输出方式,一种获取异常对象后用printStackTrace()方法输出;一种直接print输出异常对象
如果需要捕获多重异常,需要使用多个catch块
语法 :try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}catch(异常类型3 异常的变量名3){
// 程序代码
}
- throw throws
throw 语句用于抛出明显的异常。
语法:throw ThrowableInstance;
这里的ThrowableInstance一定是 Throwable 类类型或者 Throwable 子类类型的一个对象。简单的数据类型,例如 int ,char ,以及非 Throwable 类,例如 String 或 Object ,不能用作异常。
有两种方法可以获取 Throwable 对象:在 catch 子句中使用参数或者使用 new 操作符创建。程序执行完 throw 语句之后立即停止; throw 后面的任何语句不被执行,最邻近的 try 块用来检查它是否含有一个与异常类型匹配的 catch 语句。
throws 语句用来声明方法可能会抛出哪些异常
语法:public void 方法名(参数) throws 异常名
异常名可以父类Exception,也可以时子类,也可以自定义异常名
简单说:throws用于暴露异常;throw用于抛出异常。
- finally
finally里通常用来关闭资源。比如:数据库资源,IO资源等。try是一个独立的代码块,在其中定义的变量只在该变量块中有效。如果在try以外继续使用,需要在try建立引用。在try对其进行初始化。IO,Socket就会遇到。finally代码块一般都会执行到,除非前面执行了System.exit(0)。
注意:如果finally 块与一个try联合使用,finally块将在try结束之前执行。
对异常处理的一些建议:
(1).声明多个异常时,建议声明更为具体的异常,这样异常处理可以更加具体;
(2).声明了几个异常,就对应几个catch块。如果多个catch块中的异常出现继承关系时,父类异常catch块放在最下面。
(3).进行catch处理时,catch中一定要定义具体处理方式,不要简单定义一句e.printStackTrace(),也不要简单书写一条输出语句。
try, catch,finally ,return 执行顺序
-
执行try,catch , 给返回值赋值
-
执行finally
-
return
例子:
public class Test1 {
public static void main(String[] args) {
int a=12;
int b=2;
int c=0;
try {
System.out.println("66666");//1
c=a/b;
System.out.println("我在这里");//2
return;
}catch (ArithmeticException e){
e.printStackTrace();
System.out.println("异常为"+e);//3
}
finally {
System.out.println("这里也是我");//4
}
System.out.println(c);
}
}
在没有异常出现的时候,1 2 4语句都会正常运行不会收到return的影响
注意:在出现异常的时候,try里面异常语句的后面都不会执行,1 3 4语句执行
6.自定义异常:
项目中会出现特有的问题,而这些问题并未被java封装成异常对象;那么这些特有的问题可以按照java对问题封装的思想进行自定义的异常封装;这就是自定义异常。 自定义异常时,自定义异常类必须继承Exception类或其子类,如:RuntimeException。因为只有异常体系中才具有处理问题的特性。
例子:
import java.util.Scanner;
//1.编写程序接收用户输入分数信息,如果分数在0—100之间,输出成绩。如果成绩不在该范围内,抛出异常信息,提示分数必须在0—100之间。
//要求:使用自定义异常实现
public class Test4 {
public static void main(String[] args) throws MyException {
Scanner s = new Scanner(System.in);
System.out.println("输入分数:");
int score = s.nextInt();
MyException myException = new MyException();
try {
myException.chen(score);
} catch (MyException e) {
System.out.println("输入错误!分数必须在0—100之间");
e.printStackTrace();
System.out.println("用点心吧!");
}
}
}
class MyException extends Exception{
public void chen(int score) throws MyException{
if(score>0&&score<100){
System.out.println("您的成绩为:"+score);
}
else{
throw new MyException();
}
}
}
这里自定义了一个MyException异常,要注意任何自己自定义的异常类都要继承父类Exception,否则自己定义的异常类会报错。