java Exception(异常)学习笔记

297 阅读4分钟

1.什么是异常

异常就是代码中出现的问题。
代码中经常会出异常,我们要考虑的是如果出现异常应该怎么解决,而不是防止出现异常。

2.异常体系结构

image.png

Error:代表系统级别的错误(属于严重的问题),比如内存溢出,或者什么硬件问题等等。我们代码层面管不了,所以不用管。

3.Exception

  • Exception:代表程序可能出现的问题。我们通常会用Exception和它的子类来封装程序可能出现的问题。 Eexception包含两类异常,分别是RuntimeExcepton(运行时异常)和其它异常(编译时异常)。

  • RuntimeException:只有程序运行的时候才会发生,编译阶段不会出现异常。比如下面图片中的数组索引越界异常,因为程序没有执行,所以还没有发生异常:

image.png

编译时异常:这类异常在编译阶段就发生,比如下面图片中就出现了一个编译时异常,因为属于编译时异常,所以程序没有运行就发生了,idea就给出了error提示:

image.png image.png

4.两类异常的区别

  • 编译时异常:提醒程序员检查本地信息,比如下面代码:

image.png 这里的作用就是提醒程序员a.txt文件是否存在,如果不存在就会出现异常。

  • RuntimeException(运行时异常):代码出错而导致的问题。

5.异常的作用

  • 异常是查询bug的关键信息。
  • 异常可以作为方法内部的特殊返回值,以便通知调用者底层的执行情况。

第二个作用怎么理解呢?
比如我有个GrilFriend类:

package com.akbar.exception;

public class GrillFried {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        if (age <20 || age > 30) {
            throw new RuntimeException();
        }
        this.age = age;
    }
}

我给GrillFried赋值的时候,如果年龄小于20或者大于30就通知调用者执行情况:

package com.akbar.exception;

public class HandlerException {

    public static void main(String[] args) {
        GrillFried grillFried = new GrillFried();

        grillFried.setName("小惠惠");
        grillFried.setAge(15);
    }
}

调用者可以自由选择异常处理方式,比如:

  • 打印在控制台上(默认)
  • 自己悄悄处理掉

6.异常的处理方式

  • jvm默认处理方式(把异常信息用红色字体输出,程序会停止)
  • 自己处理
  • 抛出异常(交给调用者)

自己处理异常(捕获异常)

image.png 比如:

package com.akbar.exception;

public class HandlerException {

    public static void main(String[] args) {
        GrillFried grillFried = new GrillFried();

        grillFried.setName("小惠惠");

        try {
            grillFried.setAge(15);
        } catch (RuntimeException e) {
            System.out.println("年龄必须在20到30之间");
        }

        System.out.println("姓名:" + grillFried.getName() + " 年龄:" + grillFried.getAge());
    }
}

也可以写多个catch:

package com.akbar.exception;

public class HandlerException {

    public static void main(String[] args) {
        GrillFried grillFried = new GrillFried();

        grillFried.setName("小惠惠");

        try {
            grillFried.setAge(15);
        } catch (ArithmeticException e) {
            System.out.println("年龄必须是数字");
        } catch (RuntimeException e) {
            System.out.println("年龄必须在20到30之间");
        }

        System.out.println("姓名:" + grillFried.getName() + " 年龄:" + grillFried.getAge());
    }
}

7.异常中常见的方法

image.png 比如:

package com.akbar.exception;

public class HandlerException {

    public static void main(String[] args) {
        GrillFried grillFried = new GrillFried();

        grillFried.setName("小惠惠");

        try {
            grillFried.setAge(15);
        } catch (RuntimeException e) {
            e.printStackTrace();
        }

        System.out.println("姓名:" + grillFried.getName() + " 年龄:" + grillFried.getAge());
    }
}

8.抛出异常

image.png

9.自定义异常

我想验证grillFried的属性,比如女朋友的名字必须是英文,年龄必须在20到30之间:

分别定义两个自定义异常分别表示姓名的问题和年龄的问题:

NameFormatException:

package com.akbar.exception;

public class NameFormatException extends RuntimeException {

    /*
    * 如果是运行时异常,要继承RuntimeException
    * 如果时编译时异常,要继承Exception
    * */
    public NameFormatException() {
    }

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

AgeFormatException:

package com.akbar.exception;

public class AgeFormatException extends RuntimeException{
    /*
    * 如果是运行时异常,要继承RuntimeException
    * 如果时编译时异常,要继承Exception
    * */
    public AgeFormatException() {
    }

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

注意:

  • 如果是运行时异常,要继承RuntimeException
  • 如果是编译时异常,要继承Exception

然后在GrilFriend中进行校验:

package com.akbar.exception;

public class GrillFried {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        if (name.isEmpty()) {
            throw new NameFormatException("姓名不能为空!");
        }

        // 姓名只能是英文字母
        for (int i = 0; i < name.length(); i++) {
            char c = name.charAt(i);
            if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) {
                throw new NameFormatException("姓名只能是英文字母!");
            }
        }
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        if (age <20 || age > 30) {
            throw new AgeFormatException("年龄必须在20到30之间!");
        }
        this.age = age;
    }
}

然后在main方法中进行捕获异常并处理:

package com.akbar.exception;

public class HandlerException {

    public static void main(String[] args) {
        GrillFried grillFried = new GrillFried();

        try {
            grillFried.setName("小惠惠");
            grillFried.setAge(15);
        } catch (NameFormatException e) {
            System.out.println("姓名格式错误:" + e.getMessage());
        } catch (AgeFormatException e) {
            System.out.println("年龄格式错误:" + e.getMessage());
        } catch (RuntimeException e) {
            System.out.println("其它错误:" + e.getMessage());
        }

        System.out.println("姓名:" + grillFried.getName() + " 年龄:" + grillFried.getAge());
    }
}

补充说明:Java的异常分为两大类:

  1. 检测异常(checked exception):必须显式处理的异常
  2. 非检测异常(unchecked exception):RuntimeException及其子类,这类异常不强制要求显式处理