异常处理

Java异常类型

所有异常类型都是Throwable的子类,Throwable把异常分成两个不同分支的子类Error和Exception。


Error类型的异常表示运行应用程序中较严重问题。大多数错误表示代码运行时 JVM(Java 虚拟机)出现的问题。

Exception类型是程序本身可以处理的异常,它又分为非运行时异常和运行时异常(RuntimeException即运行期问题)。非运行时异常需要手动添加捕获及处理语句,如果不处理,编译则不通过;运行时异常是jvm正常运行期间抛出的错误,这些异常是不检查异常(编译器不会去检查它),一般由程序逻辑错误所引起,是代码本身有问题,这种问题我们不处理。

如果程序出了问题,并且我们没有做任何处理,最终会被默认处理程序处理,默认处理程序会把异常的信息打印在控制台,同时程序停止运行。

对于所有的可查异常,java规定:方法必须捕捉,如果不捕捉,则必须声明抛出异常

java内置异常类

非检查性异常


ArithmeticException:算数错误。如,一个整数"除以零"

ArrayIndexOutOfBoundsException:数组下标出界。

ArrayStoreException:数组元素赋值类型不兼容

ClassCastException:非法强制转换类型

IllegalArgumentException:向方法传递了一个不合法或不正确的参数

IllegalMonitorStateException:非法监控操作,如等待一个未锁定线程

IllegalStateException:环境应用状态不正确

IllegalThreadStateException:请求操作与当前线程状态不兼容

IndexOutOfBoundsException:某些类型索引超出范围

NegativeArraySizeException:应用程序试图创建大小为负的数组

NullPointerException:当应用程序试图在需要对象的地方使用 null 时,抛出该异常

NumberFormatException:字符串到数字格式的非法转换

SecurityException:由安全管理器抛出的异常,指示存在安全侵犯

StringIndexOutOfBoundsException:索引为负,或者超出字符串的大小

UnsupportedOperationException:遇到不支持请求的操作

检查行异常


ClassNotFoundException:找不到相应的类

CloneNotSupportedException:试图克隆一个无法实现 Cloneable 接口的对象

IllegalAccessException:对一个类的访问被拒绝

InstantiationException:试图创建一个抽象接口或抽象类的对象

InterruptedException:一个线程被另一个线程中断

NoSuchFieldException:请求的变量不存在

NoSuchMethodException:请求的方法不存在


try...catch

try/catch关键字可以捕获异常

try{

可能出现问题的代码

}catch(异常名 变量){

针对问题的处理

}

一旦异常被引发,程序控制转到catch块,执行了catch语句后,程序控制从整个try/catch机制的下面一行继续。一个catch语句不能捕获另一个try声明所引发的异常(除非是嵌套的try语句情况)。不能单独使用try。构造catch语句的目的是不中断程序的运行。

try里面定义的属性只能在try块中使用

class Demo{

public static void main(String[] args){

int a,b;

try{

a=5;

b=0;

System.out.println(a/b);

}catch(ArithmeticException e){

System.out.println("Division by zero");

}

System.out.println("After catch exception");

}

}

结果为:Division by zero

After catch exception


某些情况下单个代码可能引起多个异常,处理这种情况可以定义更多的catch子句,每个子句捕获一种类型的异常。

当异常被引发,每一个catch子句被依次检查,匹配上异常类型的子句执行,然后代码从try/catch块后面开始继续。

注意:异常子类必须在它们父类前面(如果不这样,编译器会报错),这是因为运用父类的catch语句将捕获该类型及其所有子类类型的异常。

class Demo{

public static void main(String[] args){

int[] a={1,2};

try{

a[34]=5;

}catch(ArrayIndexOutOfBoundsException e){

     System.out.println("ArrayIndexOutOfBoundsException catch");

}catch(Exception e){

     System.out.println("Exception catch");

}

System.out.println("After catch exception");

}

}

结果为:ArrayIndexOutOfBoundsException catch

After catch exception

try语句可以被嵌套,也就是说一个try语句可以在另一个try语句内部。

建议最好不要嵌套

public class Demo {

 public static void main(String[] args) {

   try{

     int a=args.length;

     int b=42/a;

     System.out.println("a="+a);

    try{

       if(a==1)

       a=a/(a-a);

       if(a==2){

         int c[]={1};

         c[43]=9;

       }

     }catch(ArrayIndexOutOfBoundsException e){

       System.out.println("ArrayIndexOutOfBounds:"+e);

     }

   }catch(ArithmeticException e){

     System.out.println("Divide by:"+e);

   }

 }

}

结果:Divide by:java.lang.ArithmeticException: / by zero

如果a=1,则结果为:a=1
Divide by:java.lang.ArithmeticException: / by zero
如果a=2,则结果为:a=2
ArrayIndexOutOfBounds:java.lang.ArrayIndexOutOfBoundsException: 43
粗体的try语句嵌套在了外面这个大的try块中,内部的try块不含有处理这个异常的catch语句,它将把异常传给外面的try块,在那里异常被检查是否与之匹配,这个过程将继续直到匹配成功。如果没有catch语句与之匹配,java运行时系统将自动处理这个异常。

finally

finally块无论有没有异常抛出都会执行,所以finally一般用来关闭资源。每个try语句至少,需要一个catch或finally字句。finally块可有可无,不作强制要求。

当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。如果finally语句块中也有return语句,那么直接从finally中返回了,而且finally中的return会使抛出的异常丢失,所以不建议在finally中ruturn.。finally中抛出的异常会覆盖catch中抛出的异常。

在以下4种特殊情况下,finally块不会被执行:

1)在finally语句块中发生了异常。

2)在前面的代码中用了System.exit()退出程序。

3)程序所在的线程死亡。

4)关闭CPU。

public class Demo {

public static void main(String[] args) {

int[] a={1,2};

try{

a[34]=5;

}catch(ArrayIndexOutOfBoundsException e){

System.out.println("Exception catch");

}finally{

System.out.println("finally");

}

}

}

结果为:Exception catch

finally

throw

有时候有些错误在jvm看来不是错误,系统就不会抛出异常(如输入的年龄小于0),这时候我们需要手动引发异常。程序可以用throw语句抛出一个明确的异常。程序在throw语句之后立即停止,后面的任何语句不被执行,然后在包含它的所有try块中(可能在上层调用函数中)从里向外寻找含有与其匹配的catch子句的try块

异常是异常类的实例对象,我们可以创建异常类的实例对象通过throw语句抛出

用法:throw 异常对象;

有两种获得异常对象的方法:在catch子句中捕获到的异常对象或者用new操作符创建

public class Demo {
static void demo(){
try{
throw new ArithmeticException("demo");//新实例化的异常对象
}catch(ArithmeticException e){
System.out.println("Caught inside demo");
throw e; //捕获到的异常对象e
}
}

public static void main(String[] args) {
try{
demo();
 }catch(ArithmeticException e){
System.out.println("Caught"+e);
}
}
}
结果为:Caught inside demo
Caughtjava.lang.ArithmeticException:demo
注意:throw new ArithmeticException();中new用来构造ArithmeticException实例。所有java内置的运行异常有两个构造方法:一个没有参数,一个带有一个字符串参数。当用第二种形式时,参数描述指定异常的字符串(抛出异常的原因),如果对象用作print()或println()输出时,该字符串被显示(打印异常原因)。

throw是语句抛出异常。它不可以单独使用,要么与try…catch配套使用,要么与throws配套使用。

throws

throws 是方法抛出异常,在一个方法中可能出现了某些异常,我们没法去处理它,然后就将这个异常向上抛出。如果一个方法中没有捕获某个检查性异常的语句,则该方法必须使用 throws 关键字来声明,异常的处理则交由它的调用者。throws 关键字放在方法声明的尾部。例如汽车出现了故障时,汽车不会自己去处理这个异常,而是交给开车的人来处理。

如果一个方法声明了throws,然后方法里面有try...catch块,直接catch异常的引用不再throws(即上层调用者不会去处理这个异常)。

Throws抛出异常的规则:

1) 如果是不可查异常(unchecked exception),即Error、RuntimeException或它们的子类,那么可以不使用throws关键字来声明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出。

2)必须声明方法可抛出的任何可查异常(checked exception)。即如果一个方法可能出现可查异常,要么用try-catch语句捕获,要么用throws子句声明将它抛出,否则会导致编译错误

3)当抛出了异常,该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候,应该继续抛出。

4)调用方法必须遵循任何可查异常的处理和声明规则。若覆盖一个方法,则不能声明与覆盖方法不同的异常。声明的任何异常必须是被覆盖方法所声明异常的同类或子类。

一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。

public class Demo {

public static void test() throws ArithmeticException {

   throw new ArithmeticException();

}


public static void main(String[] args) {

try{

test();

}catch(ArithmeticException e){

System.out.println("Caught:"+e);

}

}

}

结果为:Caught:java.lang.ArithmeticException: / by zero

在test()后面声明throws来引发ArithmeticException异常,在main方法中必须定义一个try/catch语句来捕获该异常

通过throw抛出异常后,如果想在上一级代码中捕获并处理异常,需要在抛出异常方法中使用throws关键字在方法声明中指明要跑出的异常。如果要捕获throw抛出的异常,必须使用try/catch语句

自定义异常

系统中有些错误是符合Java语法的,但不符合逻辑,此时就可以自定义异常。如果异常中没有我们想要的异常,也剋有自定义一个异常。比如人的性别是中性时需要抛出异常,但是一直异常中没有这个异常,这时候我们可以自定义一个异常抛出。

在 Java 中可以自定义异常。所有异常都必须是 Throwable 的子类。如果希望写一个检查性异常类,则需要继承 Exception 类。如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。

一般情况下我们都会直接继承Exception类

Exception类没有定义任何方法,它继承了Throwable提供的一些方法。

Throwable 类的主要方法:


String getMessage():返回关于发生的异常的详细信息

Throwable getCause():返回一个Throwable 对象代表异常原因

String toString():使用getMessage()的结果返回类的串级名字

void printStackTrace():打印toString()结果和栈层次到System.err,即错误输出流。

StackTraceElement [] getStackTrace():返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底。

Throwable fillInStackTrace():用当前的调用栈层次填充Throwable 对象栈层次,添加到栈层次任何先前信息中。

Java自定义异常的使用要经历如下四个步骤:

1、定义一个类继承Throwable或其子类。

2、添加构造方法(当然也可以不用添加,使用默认构造方法)。

3、在某个方法类抛出该异常。

4、捕捉该异常。


public class MyException extends Exception { //自定义的MyException异常

}


public class Demo {

static void compute(int a) throws MyException{

System.out.println("Called compute("+a+")");

if(a>10)

throw new MyException();

System.out.println("Normal exit");

}

public static void main(String[] args) {

try{

compute(1);

compute(20);

}catch(MyException e){

System.out.println("Caught:"+e);

}

}

}

结果为:Called compute(1)

Normal exit

Called compute(20)

Caught:com.DE.MyException



推荐阅读更多精彩内容