一.什么是异常?
代码程序在运行时有可能会发生各种非正常的状况,如程序运行时磁盘空间不足,网络连接中断,被加载的类不存在等,针对这些非正常的情况,Java语言中引入了异常,并且以异常类的形式对这些非正常情况进行封装,通过异常处理机制对程序运行时发生的各种问题进行处理。
二.异常体系
异常类都来自于Throwable类
下图表示Java内置的异常类之间的继承关系:
①顶层类Throwable派生出两个重要的子类,Error和Exception
②Error指的是Java运行时内部错误和资源耗尽错误,应用程序不抛出此类异常,这种内部错误一旦出现,除了告知用户并且程序终止之外,再无能为力。但这种情况很少出现。
③Exception是所有异常类的父类
④Exception类又派生出一个运行时异常类,运行时异常类就派生出很常见的数组下标越界异常,空指针异常类等
如下代码:
public class Test {
public static void main(String[] args) {
int result=func(2,0);
System.out.println(result);
}
public static int func(int x,int y){
return x/y;
}
}
运行结果:
上述代码就产生了算术异常,代码中出现了被0除的错误。所以在这个异常发生后,程序会立即结束,无法继续向下执行。
Throwable常用方法:
方法声明 | 功能描述 |
---|---|
String getMessage | 返回此throwable的详细消息字符串 |
void printStackTrace | 将此throwable及其追踪输出至标准错误流 |
void printStackTrace(PrintStream s) | 将此throwable及其追踪输出到指定的输出流 |
三.异常的基本用法
1.捕获异常
基本语法:
try{
//可能出现异常的语句
}catch(异常类型 异常对象){
//......
}finally{
//.......
}
catch代码块中放的是出现异常后的处理行为
finally代码块中的代码用于处理善后工作,在最后执行
其中catch和finally可以根据实际情况选择加或者不加
代码示例1(不处理异常):
public class Test {
public static void main(String[] args) {
int[] arr={1,2,3};
System.out.println("before");
System.out.println(arr[100]);
System.out.println("after");
}
}
执行结果:
代码示例2(使用try catch处理后):
public class Test {
public static void main(String[] args) {
int[] arr={1,2,3};
try {
System.out.println("before");
System.out.println(arr[100]);
System.out.println("after");
}catch(ArrayIndexOutOfBoundsException e){
e.printStackTrace();
}
System.out.println("after try catch");
}
}
执行结果:
仔细观察上述两个代码运行结果,可以发现一旦try中出现异常,那么try中的代码块就不会再执行,而是交给catch中的代码来执行。catch执行完毕会继续往下执行。
代码示例3(异常类型要匹配):
public class Test {
public static void main(String[] args) {
int[] arr={1,2,3};
try{
System.out.println("before");
arr=null;
System.out.println(arr[100]);
System.out.println("after");
}catch(ArrayIndexOutOfBoundsException e){
e.printStackTrace();
}
System.out.println("after try");
}
}
执行结果:
上述代码catch不能捕获到空指针异常,因为异常捕获类型不匹配。所以catch可以有多个,或者用一个catch捕获所有异常(因为Exception类是所有异常类的父类,所以可以用Exception类捕获所有异常)。
代码示例4(一个catch捕获所有异常):
public static void main(String[] args) {
int[] arr={1,2,3};
try{
System.out.println("before");
arr=null;
System.out.println(arr[100]);
System.out.println("after");
}catch(Exception e){ //可以捕获所有异常
e.printStackTrace();
}
System.out.println("after try");
}
执行结果:
代码示例5(finally用法):
public class Test {
public static int func() {
try {
int a = 10/0;
return 88;
}catch (ArithmeticException e) {
e.printStackTrace();
System.out.println("算术异常");
return 8;
}finally {
return 9; //如果将finally中return 9屏蔽掉,输出返回的是catch中的8
}
}
public static void main(String[] args) {
int ret = func();
System.out.println(ret);
System.out.println("after");
}
}
执行结果:
注意:尽量不要将return语句放在finally中,因为会抑制catch中或者try中的return语句
2.异常处理流程
①程序先执行try中的代码块
②如果try中的代码块出现异常,就会结束try中的代码,看和catch中的异常类型是否匹配
③如果找到匹配的异常类型,就会执行catch中的代码
④如果没有找到,就会将异常向上传递到上层调用者
⑤无论是否找到匹配的异常类型,finally中的代码都会被执行
⑥如果上层调用者也没有处理异常,就继续向上传递
⑦一直到main方法也没有合适的处理异常,就会由JVM来进行处理,此时程序就会异常终止
3.抛出异常(throw关键字)
除了Java内置类会抛出一些异常之外,也可以手动抛出某个异常,使用throw关键字。
代码示例:
public class Test {
public static void main(String[] args) {
System.out.println(divide(10,0));
}
public static int divide(int x,int y){
if(y==0){
throw new ArithmeticException("抛出除 0 异常");
}
return x/y;
}
}
执行结果:
4.异常说明(throws关键字)
在处理异常时,通常希望知道这段代码中究竟会出现哪些可能的异常,可以使用throws关键字,把可能抛出的异常显示的标注在方法定义的位置,提醒调用者要注意捕获这些异常。
代码示例:
public class Test {
public static void main(String[] args) {
System.out.println(divide(10,0));
}
public static int divide(int x,int y)throws ArithmeticException{
if(y==0){
throw new ArithmeticException("抛出除 0 异常");
}
return x/y;
}
}
四.自定义异常类
实际生活中有些场景需要对异常类进行扩展,创建合适的异常类
代码示例:
class UserError extends Exception{ //自定义异常类继承Exception类
public UserError(String message){
super(message);
}
}
class PasswordError extends Exception{
public PasswordError(String message){
super(message);
}
}
public class Test {
public static void main(String[] args) {
try{
login("admin","123456");
}catch( UserError userError){
userError.printStackTrace();
}catch(PasswordError passwordError ){
passwordError.printStackTrace();
}
}
public static void login(String username,String password)throws UserError,PasswordError{
if(!Test.username.equals(username)){
throw new UserError("用户名错误");
}
if(!Test.password.equals(password)){
throw new PasswordError("密码错误");
}
System.out.println("登录成功");
}
注意:
①自定义异常类通常会继承自Exception类或者RuntimeException类
②继承自Exception类的异常默认是受查异常(编译时期异常)
③继承自RuntimeException的异常默认是非受查异常