一.简介
程序在运行过程中难免会遇到不正常的情况,比如内存溢出、堆栈溢出、类型转换错误等,java把这些问题用对象进行了封装,帮助我们定位问题。如控制台显示
Exception in thread "main" java.lang.StackOverflowError
at ErrorAndException.throwError(ErrorAndException.java:5)
at ErrorAndException.main(ErrorAndException.java:24)
则说明,jvm由于线程请求栈的深度超过当前Java虚拟机栈的最大深度而抛出了StackOverflowError。
二.体系
1.Error(错误):是程序中无法处理的错误,表示运行应用程序中出现了严重的错误。此类错误一般表示代码运行时JVM出现问题。通常有VirtualMachineError(虚拟机运行错误)、NoClassDefFoundError(类定义错误)等。比如说当jvm耗完可用内存时,将出现OutOfMemoryError。此类错误发生时,JVM将终止线程。
这些错误是不可查的,非代码性错误。因此,当此类错误发生时,应用不应该去处理此类错误。
2.Exception(异常):程序本身可以捕获并且可以处理的异常。
Exception这种异常又分为两类:运行时异常和编译异常。
- 运行时异常(不受检异常):RuntimeException类极其子类表示JVM在运行期间可能出现的错误。比如说试图使用空值对象的引用(NullPointerException)、数组下标越界(ArrayIndexOutBoundException)。此类异常属于不可查异常,一般是由程序逻辑错误引起的,在程序中可以选择捕获处理,也可以不处理。
- 编译异常(受检异常):Exception中除RuntimeException极其子类之外的异常。如果程序中出现此类异常,比如说IOException,必须对该异常进行处理,否则编译不通过。在程序中,通常不会自定义该类异常,而是直接使用系统提供的异常类。
3.可查异常与不可查异常:java的所有异常可以分为可查异常(checked exception)和不可查异常(unchecked exception)。
- 可查异常:编译器要求必须处理的异常。正确的程序在运行过程中,经常容易出现的、符合预期的异常情况。一旦发生此类异常,就必须采用某种方式进行处理。除RuntimeException及其子类外,其他的Exception异常都属于可查异常。编译器会检查此类异常,也就是说当编译器检查到应用中的某处可能会出现此类异常时,将会提示你处理本异常——要么使用try-catch捕获,要么使用throws语句抛出,否则编译不通过。
- 不可查异常:编译器不会进行检查并且不要求必须处理的异常,也就说当程序中出现此类异常时,即使我们没有try-catch捕获它,也没有使用throws抛出该异常,编译也会正常通过。该类异常包括运行时异常(RuntimeException极其子类)和错误(Error)。
三.处理机制
在java应用中,异常的处理机制分为抛出异常和捕获异常。
抛出异常:当一个方法出现错误而引发异常时,该方法会将该异常类型以及异常出现时的程序状态信息封装为异常对象,并交给本应用。运行时,该应用将寻找处理异常的代码并执行。任何代码都可以通过throw关键词抛出异常,比如java源代码抛出异常、自己编写的代码抛出异常等。
捕获异常:一旦方法抛出异常,系统自动根据该异常对象寻找合适异常处理器(Exception Handler)来处理该异常。所谓合适类型的异常处理器指的是异常对象类型和异常处理器类型一致。
对于不同的异常,java采用不同的异常处理方式:
- 运行异常将由系统自动抛出,应用本身可以选择处理或者忽略该异常。
- 对于方法中产生的Error,该异常一旦发生JVM将自行处理该异常,因此java允许应用不抛出此类异常。
- 对于所有的可查异常,必须进行捕获或者抛出该方法之外交给上层处理。也就是当一个方法存在异常时,要么使用try-catch捕获,要么使用该方法使用throws将该异常抛调用该方法的上层调用者。
3.1抛出异常
- throws抛出异常
如果一个方法可能抛出异常,但是没有能力处理该异常或者需要通过该异常向上层汇报处理结果,可以在方法声明时使用throws来抛出异常。这就相当于计算机硬件发生损坏,但是计算机本身无法处理,就将该异常交给维修人员来处理。
public methodName throws Exception1,Exception2….(params){
}
其中Exception1,Exception2…为异常列表一旦该方法中某行代码抛出异常,则该异常将由调用该方法的上层方法处理。如果上层方法无法处理,可以继续将该异常向上层抛。
- throw抛出异常
在方法内,用throw来抛出一个Throwable类型的异常。一旦遇到到throw语句,后面的代码将不被执行。然后,便是进行异常处理——包含该异常的try-catch最终处理,也可以向上层抛出。注意我们只能抛出Throwable类和其子类的对象。
throw newExceptionType;
比如我们可以抛出:throw new Exception();也有时候我们也需要在catch中抛出异常,这也是允许的,比如说:
Try{
//可能会发生异常的代码
}catch(Exceptione){
throw newException(e);
}
3.2捕获异常
监控区一旦发生异常,则会根据当前运行时的信息创建异常对象,并将该异常对象抛出监控区,同时系统根据该异常对象依次匹配catch子句,若匹配成功(抛出的异常对象的类型和catch子句的异常类的类型或者是该异常类的子类的类型一致),则运行其中catch代码块中的异常处理代码,一旦处理结束,那就意味着整个try-catch结束。含有多个catch子句,一旦其中一个catch子句与抛出的异常对象类型一致时,其他catch子句将不再有匹配异常对象的机会。
try {
//可能产生的异常的代码区
}catch (ExceptionType1 e) {
//捕获并处理try抛出异常类型为ExceptionType1的异常
}catch (ExceptionType2 e){
//捕获并处理try抛出异常类型为ExceptionType2的异常
}finally{
//无论是出现异常,finally块中的代码都将被执行
}
四.测试
public class Test {
public static void main(String[] args) {
System.out.println("i="+fun());
}
private static int fun() {
int i = 0;
try{
int j = 1/0;
}catch(ArithmeticException ae){
System.out.println("除数为0了");
return 0;
}finally{
System.out.println("发生错误");
}
return 1;
}
}
public class Test {
public static void main(String[] args) {
System.out.println("i="+fun());
}
private static int fun() {
int i = 0;
try{
int j = 1/0;
}catch(ArithmeticException ae){
System.out.println("除数为0了");
return 0;
}finally{
System.out.println("发生错误");
return 1;
}
}
}
五.自定义异常
public class NegativeException extends Exception{
public NegativeException(String message) {
super(message);
}
}
public class Basic {
public void fun(int [] a,int index ) throws NegativeException{
if(a == null){
throw new NullPointerException("空指针异常");
}
if(index > a.length){
throw new ArrayIndexOutOfBoundsException("数组下标越界");
}
if(index <0){
throw new NegativeException("数组下标为负数");
}
}
public static void main(String[] args) {
Basic basic = new Basic();
int [] a = {1,2,3,4,5};
try{
basic.fun(a,-1);
}catch (Exception e){
System.out.println(e.getMessage());
//数组下标为负数
System.out.println( e.toString());
//NegativeException: 数组下标为负数
System.out.println(e);
//NegativeException: 数组下标为负数
e.printStackTrace();
// NegativeException: 数组下标为负数
//at Basic.fun(Basic.java:10)
//at Basic.main(Basic.java:17)
}
}
}
六.注意事项
1.子类在覆盖父类方法时,父类的方法如果抛出了异常, 那么子类的方法只能抛出父类的异常或者该异常的子类。
2.如果父类抛出多个异常,那么子类只能抛出父类异常的子集。
3.如果父类的方法没有抛出异常,那么子类覆盖时绝对不能抛,就只能try。
七.异常码
public enum ExceptionCode {
NullPointerException("100000","空指针异常"),
ArithmeticException("100001","算数异常");
private String code;
private String desc;
ExceptionCode(String code, String desc) {
this.code = code;
this.desc = desc;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
public class MyException extends RuntimeException{
private String code;
public MyException(String code,String message) {
super(message);
this.code = code;
}
}
public class Test {
public static void main(String[] args) {
try{
fun(1,0);
}catch (MyException e){
System.out.println(e.getMessage());
System.out.println(e);
e.printStackTrace();
}
}
private static int fun(int a,int b) {
if(b == 0){
throw new MyException(ExceptionCode.ArithmeticException.getCode(),ExceptionCode.ArithmeticException.getDesc());
}
return a/b;
}
}