第九节: 异常处理

之前也写了一篇关于异常的,其实只看这篇也可以,这篇要比之前那篇详细一点点。。。

异常的概述

程序不可避免的会有bug,对于一些不符合逻辑的情况,我们就可以用 异常 来处理。

一旦我们遇到了 异常(不正常、不符合逻辑) 的情况,系统就会自动的产生一个 异常类的实例化对象 来进行处理。(Java 是面向对象的语言,都是通过对象来解决问题。。。)

Java 中的异常体系如下图:

应当注意的是,我们处理异常的目的是解决这种情况下应该怎么办,怎么去补救这样的不正常的情况。 而不是捕获了这个异常之后就打印出来就完事了。

但有时候我们是真的不知到该怎么处理,这时候就可以把异常抛给调用他的方法,在更高的视角下,逻辑往往会清晰一些。。。

处理异常的2个原则:

  • 不要去捕获大级别的异常:比如 Exception。。。(应该要去捕获特定类型的异常)

    因为这样我们无法针对特殊情况进行处理,做不到个性化,往往对用户的感觉就会不好。

  • 不要去生吞异常:

    也就是不要去想着隐藏一个异常,不要去想着要忽视他,出现错误就应该要解决,而不是逃避。

try-catch-finally 处理异常

我们处理异常就是在可能出现异常的代码用 try 去把他抱起来,对于捕获到的具体异常情况在 catch 里处理。

finally 是一块必然执行的代码块,不管发生什么情况,里面的语句都会执行。(除非程序退出JVM、停电等这样的极端情况)。

catch , finally 我们只可以省略一个,但不能都省略,可以同时存在。

对于 return 和 finally 的问题,也没那么难理解

  • return 时,我们得到的返回值是 “值”。。。

    就比如: 对于基本数据类型,我们得到的是 他代表的数字值。。。

    ​ 对于引用类型,我们得到的是 引用的值,或者说是 引用指向的 那个地址。

    理解了这点,问题就不大了。。。

  • 但是 finally 里有 return 的话,就直接返回 finally 里的 return 了。

下面看下代码加深下理解:

/** * final 里的语句是必然执行的。 返回值判断只是跟返回的机制有关,或者说跟引用类型和基本数据类型有关。 * return 时,是已经 复制值了,不是复制引用,是复制引用的值。 * 对引用类型来说,就是复制他的地址值。 对基本数据类型来说,就是他的值。 * * 注意是必然的,除非程序终止的情况:包括但不限于JVM退出、电脑突然停电。。。 */
public class Demo1 {
   

    private static class Person{
   
        int age;
    }

    /** * 返回 number = 10; */
    private static int method1() {
   
        int number;
        try{
   
            number = 10;
            return number;
        }finally{
   
            number = 99;
        }
    }


    /** * 返回: person.age = 99 */
    private static Person method2() {
   
        Person person = new Person();
        try {
   
            person.age = 10;
            return person;
        } finally{
   
            person.age = 99;
        }
    }


    /** * String 是不可变的 * 返回: try string */
    private static String methodString() {
   
        String res = "";
        try {
   
            res = "try string";
            return res;
        } finally {
   
            System.out.println("finally in methodString execute...");
            res = "finally string";
        }
    }


    /** * StringBuilder ,就和上面那个 Person 的情况是类似的。 * 返回: try string finally string */
    private static StringBuilder methodStringBuilder() {
   
        StringBuilder res = new StringBuilder();
        try {
   
            res.append("try string ");
            // 如果返回类型是 String, 这里写的是 res.toString()。 那返回的是: try string
            return res;
        }finally {
   
            System.out.println("finally in methodStringBuilder execute...");
            res.append("finally string...");
        }
    }

    /** * 返回 try string */
    private static String methodStringBuilder2() {
   
        StringBuilder res = new StringBuilder();
        try {
   
            res.append("try string ");
            // 如果返回类型是 String, 这里写的是 res.toString()。 那返回的是: try string
            return res.toString();
        }finally {
   
            System.out.println("finally in methodStringBuilder execute...");
            res.append("finally string...");
        }
    }


    /** * finally 里有 return 的话,就直接返回了。。。 * 返回: "return in finally" */
    private static String meghodReturn() {
   
        try {
   
            return "return in try";
        }finally {
   
            System.out.println("finally in methodReturn execute...");
            return "return in finally";
        }
    }



    /** * 程序在fianlly前终止了,finally就不会执行。 */
    private static void method3() {
   
        try{
   
            System.out.println("method3() 已经开始执行。。。");
            System.exit(0);  // 人为的退出JVM
        }finally {
   
            // 上面退出程序了,这里是不会执行的。
            System.out.println("method3() 里 finally代码块。。。");
        }
    }


    public static void main(String[] args) {
   
// int number = method1();
// Person person = method2();
// System.out.println("number : " + number);
// System.out.println("person.age : " + person.age);

        String s = methodString();
        System.out.println("methodString() 的返回值 : " + s + "\n");
        StringBuilder stringBuilder = methodStringBuilder();
        System.out.println("methodStringBuilder() 的返回值 : " + stringBuilder + "\n");
        String s2 = methodStringBuilder2();
        System.out.println("methodStringBuilder2() 的返回值 :" + s2 + "\n");
        String s1 = meghodReturn();
        System.out.println("methodReturn() 的返回值 : " + s1 + "\n");

        method3();

    }
}

throws

在方法名后面使用,抛给调用该方法的地方,在调用者那里进行处理,可以try进行处理,也可以接着往上级抛。

在 main 线程中 throws 异常的话,就是给启动 main 线程的 JVM 进行处理了,一般进行处理的方法都是中断运行,打印错误信息到控制台。。。

throw

我们可以自己人为的,手动的抛出一个异常,就可以用 throw 来完成。(抛出的是 : 异常的实例化对象)

有时候我们需要处理不受检查的异常,也就是运行时异常,就可以使用 throw 来进行操作,把异常抛给上一级,和 throws 的功能有点像。

当然你可以在 try 里面 throw 出来异常,再在catch 里进行处理,但这样的行为感觉很傻。一般 throw 出来的异常,目的就是给他的上一级来进行处理。。。

自定义异常

从 Java 推出至今,官方指定了很多很多种的异常类型。

我们可以基于我们写程序的特定场景,自己定义一个符合场景的异常类,该类可继承自 Exception, 或者 RuntimeException, 就看理解上面 那个 Java 的异常体系了决定了,当然也可以继承另外的更具体的异常。

一般我们自己定义的异常类,都会去重写 只有1个参数的构造方法,传入的打印出来的异常信息。。。