异常概述
Java的异常机制主要依赖于try、catch、finally、throw和throws五个关键字,其中try关键字后紧跟一个花括号扩起来的代码块(花括号不可省略),简称try块,它里面放置可能引发异常的代码。catch后对应异常类型和一个代码块,用于表明该catch块用于处理这种类型的代码块。多个catch块后还可以跟一个finally块,finally块用于回收在try块里打开的物理资源,异常机制会保证finally块总被执行。throws关键字主要在方法签名中使用,用于声明该方法可能抛出的异常;而throw用于抛出一个实际的异常,throw可以单独作为语句使用,抛出一个具体的异常对象。
异常类的继承体系
Java 把所有非正常的情况分为两种:异常(Exception)和错误(Error),它们都继承 Throwable 类。
异常机制
在Java 中表示异常的接口是 Exception, 与其同一层次的还有一个Error接口,用于描述不可挽回的系统级错误,它们两个都继承自 Throwable 接口,这个接口是所有异常和错误的超接口。 在Java 中只有 Throwable 类型的实例才能被 (throw)或者捕获(catch),他是异常处理机制的基本组成类型。
Error
Error 是 程序无法处理的错误,表示运行应用程序中发生比较严重的问题。大多数错误与代码编写者的操作无关,而表示代码运行时和 JVM 出现的问题。例如:Java虚拟机运行错误,当 JVM 不再有继续执行操作所需的内存资源时, 将出现 OutOfMemoryError。这些异常发生时,Java虚拟机一般会选择线程终止 。
Exception
Java将异常分为两种,Checked异常和Runtime异常, Java认为Checked异常都是可以在编译阶段被处理的异常,所以它强制程序处理所有的Checked异常;而Runtime异常则无须处理。Checked异常可以提醒程序员需要处理所有可能发生的异常。
Runtime Exception
运行时异常 都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
Checked Exception
非运行时异常(编译异常) 是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常。 当程序中可能出现这类异常,要么用try-catch语句捕获它,要么用throws子句声明抛出它,否则编译不会通过。
抛出异常
抛出异常有三种形式,一是Throws,一个Throw,还有一种系统自动抛异常。下面它们之间的异同。
Throws
使用 Throws 声明抛出异常的思路是,当前方法不知道如何处理这种类型的异常,该异常应该由上一级调用者处理;如果main方法也不知道如何处理这种类型的异常,也可以使用 Throws 声明抛出异常,将异常交由 JVM 处理。JVM对异常的处理方法是,打印异常的堆栈信息,并终止程序运行。
Throws声明抛出只能在方法中使用,可以抛出多个异常类,多个异常类逗号隔开。
void exception() throws NullPointerException,
NumberFormatException, ArrayIndexOutOfBoundsException {
}
Throw
当程序出现错误时,系统会自动抛出异常;除此之外,Java 也允许程序自行抛出异常,自行抛出异常由 Throw 来完成。throw关键字作用是抛出一个异常,抛出的时候是抛出的是一个异常类的实例化对象,
Throws 和 Throw 使用
Throws 使用
/**
* @author leizige
*/
public class Math {
public static int div(int a, int b) throws Exception { //有异常,交给调用方处理
/* 计算,此处可能出现异常 */
return a / b;
}
}
class Test {
public static void main(String[] args) {
try {
int result = Math.div(10, 2);
} catch (Exception e) {
/* 打印异常 */
e.printStackTrace();
}
}
}
因为div使用了throws关键字声明,所以调用此方法的时候,方法必须进行异常处理。通过try...catch;
主方法抛出异常,交给JVM处理。
/**
* @author leizige
*/
public class Math {
public static int div(int a, int b) throws Exception { //有异常,交给调用方处理
/* 计算,此处可能出现异常 */
return a / b;
}
}
class Test {
public static void main(String[] args) throws Exception {
int result = Math.div(10, 0);
}
}
运行结果:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at cn.wengzi.exception.Math.div(Math.java:10)
at cn.wengzi.exception.Test.main(Math.java:17)
Throw 使用
public class MyException {
public static void main(String[] args) {
try {
/* 手动抛出异常实例化对象 */
throw new Exception("66666666");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Throws 和 Throw 的应用
例如,现在要使用一个相除的方法,但是在操作之前必须打印“运算开始”的信息,结束之后必须打印“异常结束”。
/**
* @author leizige
*/
@Slf4j
public class Math {
public static int div(int a, int b) throws Exception { //有异常,交给调用方处理
int result = 0;
/* 计算,此处可能出现异常 */
log.info("------------计算开始------------");
result = a / b;
log.info("------------计算结束------------");
return result;
}
}
class Test {
public static void main(String[] args) {
try {
int result = Math.div(10, 0);
System.err.println("计算结果:" + result);
} catch (Exception e) {
System.err.println("错误信息 : " + e);
}
}
}
执行结果:
16:12:46.188 [main] INFO cn.wengzi.exception.Math - ------------计算开始------------
错误信息 : java.lang.ArithmeticException: / by zero
以上没有计算结束,因为没有异常发生了,直接中断程序操作。所以做以下操作。
/**
* @author leizige
*/
@Slf4j
public class Math {
public static int div(int a, int b) {
log.info("------------计算开始------------");
int result = 0;
try {
/* 计算,此处可能出现异常 */
result = a / b;
} catch (Exception e) {
/* 不抛出异常 */
}
log.info("------------计算结束------------");
return result;
}
}
class Test {
public static void main(String[] args) {
try {
int result = Math.div(10, 0);
System.err.println("计算结果:" + result);
} catch (Exception e) {
System.err.println("错误信息 : " + e);
}
}
}
执行结果:
16:15:08.252 [main] INFO cn.wengzi.exception.Math - ------------计算开始------------
16:15:08.255 [main] INFO cn.wengzi.exception.Math - ------------计算结束------------
计算结果:0
以上代码虽然执行成功了,但是异常并没有抛出去! 因为在方法中已经被自动处理了。所以要抛出异常对象,给方法调用处处理,使用throw关键字。
/**
* @author leizige
*/
@Slf4j
public class Math {
public static int div(int a, int b) {
log.info("------------计算开始------------");
int result = 0;
try {
/* 计算,此处可能出现异常 */
result = a / b;
} catch (Exception e) {
throw e; //抛出异常对象
}
log.info("------------计算结束------------");
return result;
}
}
class Test {
public static void main(String[] args) {
try {
int result = Math.div(10, 0);
System.err.println("计算结果:" + result);
} catch (Exception e) {
System.err.println("错误信息 : " + e);
}
}
}
执行结果:
16:18:45.405 [main] INFO cn.wengzi.exception.Math - ------------计算开始------------
错误信息 : java.lang.ArithmeticException: / by zero