解析Java异常处理机制,你写程序的时候是不是没有处理过异常?

很多人在写程序的时候,基本用不到捕获异常(除了程序强制要求以外),因为在写一段程序实现一些功能的时候,不用异常也没有问题,有异常就说明程序错了嘛,改一改就没有了呀
一些小的功能,一些算法之类的,确实不用处理异常也没关系,但是在一些大型应用当中,捕获异常就变得尤为重要了

为什么要捕获异常?

我还记得在上课时,老师举过的一个例子,很形象
假如当我们在玩游戏的时候,突然某一个地方出了bug,比如人物角色死亡了,然后出现了空指针异常,因为当程序出现异常的时候,整个程序就会停止,不会执行接下来的代码了,那么这个游戏此时就已经奔溃了

但是我们玩游戏的时候并不会这样,玩游戏遇到bug也不陌生,那为什么游戏就没有崩溃呢?
因为我们对这些可能出现异常的地方进行了捕获,并做出了处理(在捕获异常之后,try-catch之后的代码依然会执行),比如出现异常时,提示你程序出现了异常,请上传错误之类的,然后我们点击确认或者取消,就可以接着玩了

如何捕获异常并处理异常

就是在try块中进行捕获,然后再catch中进行处理

try {
    // 可能出现异常的部分
} catch (ArithmeticException e) {
    // 对异常进行处理的部分
} finally {
    // 无论有没有异常,都会执行的部分
    // 通常用于释放资源等
    // 例如文件打开后执行某个操作出现了异常,那么最终还得把文件关闭才可
}

对异常不进行处理的情况

public class ExceptionTest {
    public static void main(String[] args) {
        int sum = 0;
        sum = 1 / 0; // 程序中不允许除0
        System.out.println("这是出现异常之后的内容,被执行到了");
    }
}

算术异常
可以看到,程序报出了ArithmeticException异常(算术异常),因为没有进行处理,所以Java启用默认处理机制,即打印异常栈信息到屏幕,并退出程序,所以异常之后的地方没有被执行到(如果是个游戏程序的话,游戏就已经崩溃了)

对异常进行处理的情况

public class ExceptionTest {
    public static void main(String[] args) {
        int sum = 0;
        try {
            // 程序中可能出现异常的地方
            sum = 1 / 0; // 程序中不允许除0
            System.out.println("这是出现异常之后的内容,被执行到了");
        } catch (Exception e) {
            // 出现异常之后处理的地方
            System.out.println("捕获到异常,异常信息为:" + e.getMessage());
        } finally {
            // 无论有没有,都会被执行到的地方
            System.out.println("这是finally执行的地方");
        }
        // try-catch块之外的地方
        System.out.println("这是try-catch之后的内容,被执行到了");
    }
}

处理算术异常
通过结果可以看到

  • 在catch中捕获到了异常,并打印了异常信息
  • 而finally中的程序也被执行到了(finally中一般用于释放资源等操作)
  • 在出现异常之后,try-catch之外的内容仍然被执行到了
  • 但是,在try块内,在出现异常的那条语句之后的语句,却不会被执行到

如果这是一个游戏程序的话,那么游戏就不会崩溃,只是我们执行的那部分出现异常,没有执行成功,在游戏中提示了一下,然后就可以接着玩了
这就是我们为什么要进行异常处理

异常类

所有的异常的类都有一个共同的父类Throwable
Java异常类体系
Error表示系统错误或资源耗尽,由Java系统自己使用,应用程序不应抛出和处理
如:内存溢出、栈溢出等
Exception表示应用程序错误,我们应该对这些异常进行捕获和处理

  • 受检异常:强制要求进行处理,否则会有编译错误
  • 未受检异常:不会出现编译错误,但是运行时还会出现错误——RuntimeException

常见的异常

异常 说明
NullPointerException 空指针异常
NumberFormatException 数字格式异常
IllegalStateException 非法状态异常
IllegalArgumentException 参数错误异常
ClassCastException 非法强制类型转换异常
IndexOutOfBoundsException 索引越界异常
ArrayIndexOutOfBoundsException 数组索引越界异常
StringIndexOutOfBoundsException 字符串索引越界异常

自定义异常

除了Java API提供的异常类之外,也可以定义自己需要的异常类,一般是继承Exception或者他的子类
和其它异常类一样,只调用了父类的构造方法,然后定义了自己的构造方法

public class MyException extends Exception {

    public MyException() {
        super();
    }

    public MyException(String message) {
        super(message);
    }

    public MyException(Throwable cause) {
        super(cause);
    }

    public MyException(String message, Throwable cause) {
        super(message, cause);
    }

    protected MyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

多个catch处理异常

先来看一个例子

public class ExceptionTest {
    public static void main(String[] args) {
        int sum = 0;
        try {
            sum = 1 / 0;
            System.out.println("这是出现异常之后的内容,被执行到了");
        } catch (ArithmeticException e) {
            System.out.println("ArithmeticException捕获到异常,异常信息为:" + e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            System.out.println("Exception捕获到异常,异常信息为:" + e.getMessage());
        } finally {
            System.out.println("这是finally执行的地方");
        }
        System.out.println("这是try-catch之后的内容,被执行到了");
    }
}

异常实例
以上代码中使用了两个catch(可以使用多个),按顺序进行匹配,当有一个catch匹配成功,那么后面的将不会再进行匹配了。
第一个catch捕获的异常是ArithmeticException,而第二个catch捕获的是Exception,Exception是ArithmeticException的父类,所以通常将父类放在子类的后面,如果放在子类的前面,则子类将不会被匹配到了。

  • e.getMessage():获取异常的信息
  • e.printStackTrace():打印异常栈,如上图红色字体显示,可以帮助我们查看到底哪里出现了异常

抛出异常

当某个方法出现异常,但这个方法不处理这个异常,那么就会将异常抛出,异常会被抛出到上一层,直到有try-catch捕获到为止

  • throw:抛出异常,强调抛出的动作
  • throws:只是声明可能出现异常,强调一种状态或趋势

throw实例

public class ExceptionTest {
    // 这个方法可能出现异常,但是不解决这个异常,所以将异常抛出,在其它地方解决
    public void test(){
        int sum = 0;
        try {
            sum = 1/ 0;
        } catch (ArithmeticException e) {
            throw new ArithmeticException("算术异常");
        }

    }
    public static void main(String[] args) {
        ExceptionTest exceptionTest = new ExceptionTest();
        try {
            exceptionTest.test();
        } catch (ArithmeticException e) {
            System.out.println(e);
            e.printStackTrace();
        }
    }
}

算术异常抛出
在test方法中出现了异常,但是没有处理,而是将异常抛出,然后再main方法中进行了捕获,并在catch中进行了处理

throws实例

public class ExceptionTest{

    public void test() throws ArithmeticException{
        int sum = 0;
        sum = 1/ 0;
    }
    public static void main(String[] args) {
        ExceptionTest exceptionTest = new ExceptionTest();
        try {
            exceptionTest.test();
        } catch (ArithmeticException e) {
            System.out.println(e);
            e.printStackTrace();
        }
    }
}

throws
结果与throw的一样,因为都发生了异常,然后将异常进行抛出

throw与throws的区别

throw:强调动作,在方法内部执行
throws:强调状态,在声明方法体上声明,并不一定会出现异常,只是一种可能
对于未受检异常,是不要求使用throws进行声明的,但对于受检异常,则必须进行声明,换句话说,如果没有声明,则不能抛出

异常处理的一般逻辑

  • 在可能出现异常的地方进行捕获
  • 如果异常可以被自己处理,就自己处理
  • 如果自己不能完全解决,就向上抛出
  • 如果自己有额外信息可以提供,有助于分析和解决问题,就应该提供
发布了35 篇原创文章 · 获赞 20 · 访问量 7080

猜你喜欢

转载自blog.csdn.net/weixin_42193813/article/details/105481836