3.1java的异常

前言:
java程序运行中难免会有异常出现,一些由用户造成,一些由随机因素造成,为了处理这些异常,java内置了一套遗产机制。
java中的异常是class
借鉴图片:
异常类的描述
由图知其有两种异常:
第一种是:Error,表示严重的错误,程序对此无能无力。
第二种是:Exception,运行的错误,表示可以被捕获并处理。

我们经常使用的异常处理就是Exception
Exception分为两种:

  1. RuntimeException以及它的子类;
  2. 非RuntimeException(包括IOException、ReflectiveOperationException等等)

java中又分为需要捕获和不需要捕获的异常:

  • 必须捕获的异常,包括Exception及其子类,但不包括RuntimeException及其子类,这种类型的异常称为Checked Exception。
  • 不需要捕获的异常,包括Error及其子类,RuntimeException及其子类。

捕获异常

捕获异常使用try…catch语句,把可能发生异常的代码放到try {…}中,然后使用catch捕获对应的Exception及其子类:

public class Main {
    public static void main(String[] args) {
        byte[] bs = toGBK("中文");
        System.out.println(Arrays.toString(bs));
    }

    static byte[] toGBK(String s) {
        try {
            // 用指定编码转换String为byte[]:
            return s.getBytes("GBK");
        } catch (UnsupportedEncodingException e) {
            // 如果系统不支持GBK编码,会捕获到UnsupportedEncodingException:
            System.out.println(e); // 打印异常信息
            return s.getBytes(); // 尝试使用用默认编码
        }
    }
}
可以使用多个catch语句

可以使用多个catch语句,每个catch分别捕获对应的Exception及其子类。JVM在捕获到异常后,会从上到下匹配catch语句,匹配到某个catch后,执行catch代码块,然后不再继续匹配。
存在多个catch情况时,catch的顺序十分重要,子类需要放在前面,否则永远也匹配不到子类。

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (IOException e) {
        System.out.println(e);
    } catch (NumberFormatException e) {
        System.out.println(e);
    }
}
finally语句

无论是否捕捉到异常,都执行的语句

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (UnsupportedEncodingException e) {
        System.out.println("Bad encoding");
    } catch (IOException e) {
        System.out.println("IO error");
    } finally {
        System.out.println("END");
    }
}

finally的特点:

  • finally可写可不写
  • finally总是最后执行
捕获多种异常

由于某些情况下,两个异常的处理方式相同,则可以用‘|’将两个异常连接起来。

catch (IOException | NumberFormatException e) { // IOException或NumberFormatException
        System.out.println("Bad input");

抛出异常

  • 打印方法调用栈:printStackTrace()方法,在抛出异常时,能够明确的看到抛出异常最初是在哪里抛出的。

  • throw语句:异常正常抛出的示例

    void process2(String s) {
        if (s==null) {
            throw new NullPointerException();//在这里抛出异常
        }
    }
    
异常转换:
```java
public class Main {
    public static void main(String[] args) {
        try {
            process1();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void process1() {
        try {
            process2();
        } catch (NullPointerException e) {
            throw new IllegalArgumentException();
        }
    }

    static void process2() {
        throw new NullPointerException();
    }
}

```
- 解释:上述代码中,执行main方法调用process1方法,process1方法调用process2方法抛出NullPointerException异常,process2方法没有捕获,抛往上层方法process1方法,process1方法捕获到空指针异常之后,重新抛出IllegalArgumentException()异常,接着被上层main方法捕获。
- 异常栈为:异常栈按顺序打印被捕获到的异常。
```bash
java.lang.IllegalArgumentException
at Main.process1(Main.java:15)
at Main.main(Main.java:5)
```
  • 如果抛出异常,不会影响finally的执行。jvm会先执行finally语句,然后抛出异常。
异常追踪

在上述代码中,异常栈中,只是指出了很浅显的异常,并没有指出最根本的原因,如何用其找到最根本的原因。
将上述代码中process2中捕获到异常后抛出异常的部分修改为:throw new IllegalArgumentException(e);
上述代码异常栈多出了Caused by的部分,这里才是最根本的原因。

java.lang.IllegalArgumentException: java.lang.NullPointerException
    at Main.process1(Main.java:15)
    at Main.main(Main.java:5)
Caused by: java.lang.NullPointerException
    at Main.process2(Main.java:20)
    at Main.process1(Main.java:13)
异常屏蔽
  • 但如果程序出现异常之后,finally在执行中又出现异常,那么最先开始的异常会被屏蔽,进而只抛出最后一个异常,也就是finally中抛出的异常。如何解决?
  • 一般情况下,我们会把所有的异常存放在origin中,并在finally中判断其是否为空,不为空则打印异常信息。
public class Main {
    public static void main(String[] args) throws Exception {
        Exception origin = null;
        try {
            System.out.println(Integer.parseInt("abc"));
        } catch (Exception e) {
            origin = e;//将异常存储在origin中
            throw e;
        } finally {
            Exception e = new IllegalArgumentException();
            if (origin != null) {//在这里判断是否之前存在异常
                e.addSuppressed(origin);
            }
            throw e;
        }
    }
}

自定义异常

java标准库常用异常
一般来说,java给出的常用异常已经够基本开发使用,但一些大型项目中需要自定义异常,最常见的做法是自定义一个BaseException作为根异常(继承自RuntimeException),并且自定义的BaseException应该提供多个构造方法。

public class BaseException extends RuntimeException {
    public BaseException() {
        super();
    }

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

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

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

NullPointerException(空指针异常)

指针在java中不存在,说法是从C语言中流传过来的,正常的说法应该是空引用。
NullPointerException是常见的错误,应早暴露早修复
避免空指针异常:应用""空字符串或空数组。
java14开始JVM可以告诉我们详细的出错信息,告诉我们Null对象是谁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值