什么是异常
程序在运行过程中发生由于外部问题导致的程序异常事件,会阻止当前方法或作用域继续执行的问题。一旦发生异常,程序就不能再继续按照预期执行下去。对于开发者来说,这无疑是致命的。我们所能做的就是从当前环境跳出,把发生的问题提交给上一层环境来进行处理,这就是抛出异常。
异常体系结构
所有的异常 Exception
和 错误 Error
都继承自 Throwable
类。
-
Error
错误 是程序无法处理的问题,属于运行程序中比较严重的问题,程序本身无法进行处理,只能尽量避免,无法用代码进行处理。这类错误大多数与代码编写者执行的操作无关,例如当内存占满时,
JVM
就会发出错误操作来终止线程运行。 -
Exception
异常 是程序本身可以处理的问题。从整体上可以可以划分为 受检异常 和 非受检异常 (即除
RuntimeException
之外的异常都是受检异常)。受检异常就是在代码编写时就必须进行异常处理,要么使用try-catch
语句捕获异常,要么就是通过throws
关键字将异常抛出,否则编译就不能通过;非受检异常也称为 运行时异常,例如常见的空指针异常NullPointerException
和数组越界异常ArrayIndexOutOfBundsException
就是运行时异常,这类异常一般只有在程序运行过程中才可能发生,因此不强制使用try-catch
语句进行异常的捕获或throws
关键字进行抛出。
如何处理异常
语法格式:
try{
//可能出现异常的代码片段
}catch(异常类型1 对象名){
//出现异常的处理操作
}catch(异常类型2 对象名){
//出现异常的处理操作
}
...
finally{
//异常的统一出口(除外部条件导致的设备中断运行和存在退出JVM操作的代码—— System.exit(0);外,finally代码块都会执行)
}
//注:catch 和 finally 块可以省略其中一个不写,不能同时不写
例如:
public static void main(String[] args) {
int x = 10;
int y = 0;
int z = x / y;
}
如果不进行异常处理,运行结果就是这个样子,程序中断,无法执行完毕。
使用 try-catch
捕获异常并做出异常处理,从运行结果可以看出,程序正常执行完毕。
public static void main(String[] args) {
try {
int x = 10;
int y = 0;
int z = x / y;
} catch (Exception e) {
System.out.println("分母不能为0"); //异常处理
}
}
try-catch-finally 执行流程
-
方法无返回值
public static void demo01() { try { System.out.println(1); System.out.println(2); System.out.println(3); return; } catch (Exception e) { System.out.println("发生了异常"); }finally { System.out.println("finally执行了"); } } 输出: 1 2 3 finally执行了
-
方法有返回值(基本类型的返回值)
public static int demo05() { int a = 10; try { a = 20; return a; //把a的值复制一份作为返回值,因此该方法的返回值为20 } catch (Exception e) { }finally { a = 30; //这里的a和返回值a是两个不同的变量,因此对原来的a进行修改不影响返回值 } return a; } 方法返回值输出: 20
-
方法有返回值(返回值是引用数据类型)
public static Person demo07(){ Person p = new Person(); try{ p.age = 18; return p; //与上个例子类似,只不过对于引用数据类型来说,这里复制的就是指向p对象的地址 }catch(Exception e){ return null; }finally { p.age = 28; //因此对p对象修改之后,返回值指向的对象还是同一个对象,所以输出为28 } } static class Person{ int age; } public static void main(String[] args) { Person p = demo07(); System.out.println(p.age); } 输出为: 28
-
System.exit(0) 退出JVM
public static void demo06() { try { int a = 10 /0; System.out.println("try语句"); } catch (Exception e) { System.out.println("发生了异常"); //退出JVM System.exit(0); //JVM退出后finally不再执行 }finally { System.out.println("finally执行了"); //JVM退出后,finally就不再执行 } } 输出: 发生了异常
-
总结
-
除外部环境(例如断电)和包含退出JVM的代码,
finally
里面的语句都会执行。 -
try-catch-finally执行流程:
- 先计算返回值,并将返回值存储起来,等待返回
- 执行 finally代码块(如果
finally
中存在return
语句,则流程提前退出,并且方法返回值不再是try或catch
中的返回值,因此不建议在这里添加return
语句) - 将之前存储的返回值返回出去
注意:返回值是在
finally
语句执行之前就确定了,不管finally
对值做任何改变,返回的值都不改变(需注意引用类型和基本类型的区别,引用类型存储的是地址,不是具体的值)
-
throw和throws区别
throws 关键字用在方法的生命上使用,表示该方法不处理异常,而交给调用该方法的地方进行处理
格式:
返回值 方法名称() throws Exception{
//业务代码
}
throw 关键字表示在程序中人为的抛出一个异常(异常处理机制其实也就是在异常产生后抛出一个异常类的实例化对象)
用法:
throw new Exception("抛出的异常");
自定义异常
编写一个类, 继承Exception,并重写一参构造方法 即可完成自定义受检异常类型。
编写一个类, 继承RuntimeExcepion,并重写一参构造方法 即可完成自定义运行时异常类型。
例如:
class MyException extends Exception{
public MyException(Stirng msg){
super(msg); //调用父类中的构造方法
//其他业务需要的代码
}
}