前言:
异常是指程序的指令没有被正常执行的情况,这些情况会让程序异常退出,在Java中有两种类别的异常:Error和Exception。
Error和Exception都是继承于Throwable类。
一.Error
Error 类对象由 Java 虚拟机生成并抛出,不可捕捉,程序本身不能处理。这种情况下会导致程序异常崩溃,如下图的Error继承结构可看出,Error下会有许多子类。例如较常见的Error异常StackOverflowError(某个线程的线程栈空间被耗尽)和OutOfMemoryError(内存溢出异常)。
二.Exception
Exception是能够在程序中被处理的,Exception分为两类,运行时异常(RuntimeException)和非运行时异常。
1.运行时异常(RuntimeException)
RuntimeException类及其子类的实例被称为运行时异常,即UnChecked Exception。
以下是RuntimeException部分子类的截图,如图有我们较为常见的NullPointerException(空指针异常)和 IndexOutOfBoundsException(数组越界异常),对于这些Runtime异常,在java编译器中是不会强制要求程序员进行处理或声明的(有些IDE可能会给可能出现Runtime异常问题的提示,但不会报错)。
2.非运行时异常
顾名思义,不是RuntimeException类及其子类的异常实例则被称非运行时异常,即Checked Exception。
以下是部分非运行时异常类的截图,如图有我们较为常见的FileNotFoundException(文件找不到的异常),IOExeption,SQLException,JSONException,TimeoutException, 对于这些非运行时异常,在java编译器中是会强制要求程序员进行处理或声明的;
对于所有的Checked异常,需要用try…catch显式的捕获并处理,如果没有try…catch这个异常,则编译出错(在一些IDE中会提示错误且强制要求修改),错误提示类似于Unhandled exception: XXXXX;
如图对可能出现IOException的地方没有使用try…catch会提示:Unhandled exception: java.io.IOException
三.抛出异常
1.throw
1) 只能被使用在方法体内 ;
2) throw是具体向外抛异常的,抛出的是一个异常的实例。
2.throws
1) throws是用在方法声明后面,表示如果抛出异常,由该方法的上层调用者来处理;
2)throws声明该方法可能会抛出的异常的类型,让它的使用者知道要捕获的异常的类型;
3)throws表示出现异常的可能性,并不代表一定会发生该异常。
- throws和throw的使用例子(声明一个可能抛出异常的方法test() ):
//在方法声明的时候使用throws声明有可能抛出的异常,如果有方法调用这个方法
//那么调用者需要通过try...catch语句处理或继续抛出Exception1和Exception3异常
public void test(int x, int y) throws Exception1, Exception3 {
try {
System.out.println("to try!");
if (x < 0) {
//在方法体内直接抛出NumberFormatException异常实例
throw new NumberFormatException();
}
}
//捕获Exception2
catch (Exception2 e) {
System.out.println("Catch Exception2 !");
}
if (x > y) {
//在方法体内直接抛出Exception3异常实例
throw new Exception3("x > y");
}
}
- 声明一个testTransfer() 方法,在testTransfer() 里调用上面的test() 方法(必须使用try...catch捕获异常或向上继续抛出异常):
调用一(使用try...catch捕获异常):
public void testTransfer() {
try {
//调用有可能有Exception1和Exception2异常抛出的test()方法
test(1, 2);
}
//必须捕获Exception1和Exception2异常,否则会报错
catch (Exception1 exception1) {
exception1.printStackTrace();
} catch (Exception3 exception3) {
exception3.printStackTrace();
}
}
调用二(使用throws向上继续抛出异常):
//使用throws继续向上抛出异常
public void testTransfer() throws Exception3, Exception1 {
//调用有可能有Exception1和Exception2异常抛出的test()方法
test(1, 2);
}
四.finally
不管有没有异常,finally 中的代码都会执行,当 try、catch 中有 return 时,finally 中的代码依然会继续执行。
先看以下代码:
System.out.println(test());
public static int test() {
int x = 1;
try {
x = 2;
return x;
} finally {
x = 3;
System.out.println(x);
}
}
执行结果:
3
2
说明:从结果可以看到,代码先执行完了try里的代码,虽然try代码块中有return语句,但是不会立马返回,先执行完return语句前的代码块,且将代码结果保存下来,然后去执行finally中的代码块,执行完finally中的代码块后将先前保存的结果返回。
五.全局处理未捕获到异常的情况
对于没有抛出的异常没有被catch住(称这个异常为Uncaught异常),将会导致应用程序崩溃。Android的默认处理方式是在程序退出前,弹出默认的强制关闭对话框,如果不想因为没有捕获到异常而弹出这个默认强制关闭对话框导致用户体验不佳,开发者可以自定义一个个性化的提示框安慰一下用户,或者重启应用程序等。
在java中有一个接口,即UncaughtExceptionHandler,该接口含有一个纯虚函数:public abstract void uncaughtException (Thread thread, Throwableex)。要自定义处理以上所诉未捕获到异常的情况,继承实现UncaughtExceptionHandler即可。
自定义MyCrashHandler的示例代码:
public class MyCrashHandler implements Thread.UncaughtExceptionHandler {
private Context mContext;
//系统默认的UncaughtException处理类
private Thread.UncaughtExceptionHandler mDefaultHandler;
@Override
public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {
//........捕抓后的处理方法块..........
}
//初始化,如果需要在全局获取,需要在Application中初始化好
public void init(Context context) {
mContext = context;
//获取系统默认的UncaughtException处理器
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
//设置MyCrashHandler为程序的默认处理器
Thread.setDefaultUncaughtExceptionHandler(this);
}
}
在Application中初始化以指定默认处理未捕获到异常情况的处理类:
public class MyCrashApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
MyCrashHandler myCrashHandler = MyCrashHandler.getInstance();
myCrashHandler .init(getApplicationContext());
}
}
步骤:
1)自定义类继承UncaughtExceptionHandler,实现uncaughtException ( )方法
2)通过setDefaultUncaughtExceptionHandler指定默认处理类