Java异常

1.什么是异常?

异常是程序在执行的过程中出现的错误。

2.异常体系

在这里插入图片描述

3.Error

系统级别异常,出现异常后无法通过程序本身解决的异常,只能重启应用程序。

4.Exception

异常出现后通过程序本身就能解决的异常。

4.1.编译时异常

除了RuntimeException外的异常都是编译异常,它是源码编译前需要处理的异常

必须明确的一点,编译时异常不是指编译时出现的异常,而是指编译前需要处理的异常,它和所有异常一样都是程序在运行期间出现的错误,编译时异常不一定出现,它是解决用户输入错误导致异常的预留方案

针对某些代码,如果在编译前不进行异常处理,那么就无法通过编译器生成字节码文件。这就是所谓的异常处理,它专门为编译时异常所生的,异常处理方式会在稍后进行讲解。

异常处理工作是程序员不得不做的,即使后来程序在运行时没有出现异常。当程序在JVM运行时真的出现这种异常,就会根据之前的异常处理方案去处理异常。

编译异常和程序员的开发水平和没有一点关系,程序员无法制造编译异常,它往往是因为用户的错误输入导致的异常,比如找不到文件,商品关联套餐不能下架(自定义异常)等等。

编译时异常是无法避免的,可遇不可求的,用户总有输入错误的时候。

常见的编译异常:
IOException:输入输出流异常
FileNotFoundException:文件找不到的异常
ClassNotFoundException:类找不到的异常
DataFormatException:数据格式化异常
NoSuchFieldException:没有匹配的属性异常
NoSuchMethodException:没有匹配的方法异常
SQLException:数据库操作异常
TimeoutException:执行超时异常

4.2.RuntimeException

运行时异常,它是源码编译前不需处理的异常,出现异常最后由JVM杀死程序。

如果出现了RuntimeException,那就表示代码设计的逻辑不够严谨,这就是程序员本身的问题了。

当然你也可以对RuntimeException做异常处理,但是你既然知道一定会出RuntimeException的,为什么不做逻辑处理,而去做异常处理呢?所以异常处理对于RuntimeException是鸡肋般的存在。

常见的运行时异常:
NullPointerException:空指针异常
ArrayIndexOutofBoundsException:数组越界异
ClassCastException:类型转换异常
IllegalArgumentException:非法的参数异常
InputMismatchException:输入不匹配

5.编译时异常的处理机制

异常处理机制有三种,分别是:

  • throws:将方法出现的异常抛给方法调用者,层层往外抛,最终抛给JVM,由JVM出手杀死程序。异常处之前的代码都会执行,异常处之后的代码不会执行。
  • try…catch:将方法出现的异常进行捕捉处理,异常处之前和之后的代码正常执行。
  • throws和try…catch相结合:将需要处理的异常的方法都用throws抛给调用者,层层往外抛,在最外层的调用者进行try…catch统一捕捉异常。

下面通过代码运行实践验证结论:

5.1.throws

代码演示:

//将异常抛给JVM
public static void main(String[] args) throws FileNotFoundException {
        f2();
    }

    //将异常抛给方法调用者main()
    public static void f2() throws FileNotFoundException {
        f1();
        System.out.println("f2...");
    }

    //将异常抛给方法调用者f2()
    public static void f1() throws FileNotFoundException {
        System.out.println("test throws");
        InputStream is = new FileInputStream("e:\\2\\abc.txt");
        System.out.println("f1...");
    }

测试结果:

在这里插入图片描述

由测试结果可以看出,异常出现处即InputStream is = new FileInputStream(“e:\2\abc.txt”),在它之前的代码都运行了,并立即杀死程序,打印异常的日志信息,在异常处之后的代码明显没有执行。

5.2.try…catch

代码演示:

public static void main(String[] args) {
        f2();
    }

    //自己捕捉异常
    public static void f2()  {
        f1();
        try {
            InputStream is = new FileInputStream("e:\\2\\abc.txt");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println("f2...");
    }

    //自己捕捉异常
    public static void f1()  {
        System.out.println("test throws");
        try {
            InputStream is = new FileInputStream("e:\\2\\abc.txt");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println("f1...");
    }

测试结果:

在这里插入图片描述
可以从输出内容看出,异常处之前的代码和之后的代码都被执行了。

关于这个异常处的定义,可要仔细说说了,在throws异常处理机制中,异常处就是出现异常的那一行代码。

可是它在try…catch里的定义可完全和throws的不一样:

在try…catch中异常处是try里面出现异常的那一行及其往后的内容。

try这个关键字可不简单,它会让JVM将出现异常的那一行及其往后的内容看作是一个整体异常处。

请往下看:

public static void main(String[] args) {
        f2();
    }

    //自己捕捉异常
    public static void f2()  {
        f1();
        try {
            InputStream is = new FileInputStream("e:\\2\\abc.txt");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println("f2...");
    }

    //自己捕捉异常
    public static void f1()  {
        System.out.println("test throws");
        try {
            int a = 10,b = 20;
            System.out.println("a:" + a);
            System.out.println("b:" + b);
            InputStream is = new FileInputStream("e:\\2\\abc.txt");
            System.out.println(sum(a, b));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println("f1...");


    }

    public static int sum(int a, int b) {
        return a + b;
    }

测试结果:
在这里插入图片描述

从输出的内容可以看出,a和b被输出了,但并没有执行sum(),这是为何?
因为a和b在异常出现的那一行(即InputStream这行)之前,而sum()这一行在InputStream这行之后,JVM认为InputStream这行和及其往后的行是一个整体异常处,所以就不往下执行sum()方法了。

所以我们不要盲目地像别人那样把将所有代码都放在try里面,要谨慎考虑啊。

那什么时候把代码都放进try里面?什么时候又不全放里面了?

答:如果出现异常的那一行代码会影响其在try里它往后的代码,那就都代码放进try里面,因为要保持程序的一致性嘛。相反,出现异常的那一行代码不会影响它往后的代码,那就不要都放进try里,都放进去,你的异常不就连累我其他代码的正常运行吗?

5.3.throws和try…catch结合

代码演示:

public static void main(String[] args) {
        try {
            f2();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    //自己捕捉异常
    public static void f2() throws FileNotFoundException {
        f1();
        InputStream is = new FileInputStream("e:\\2\\abc.txt");
        System.out.println("f2...");
    }

    //自己捕捉异常
    public static void f1() throws FileNotFoundException {
        System.out.println("test throws");
        InputStream is = new FileInputStream("e:\\2\\abc.txt");
        System.out.println("f1...");
    }

throws和try…catch相结合,可以省去重复的try…catch工作,将所有异常集中处理,让程序员开发起来变得十分方便。

6.异常处理使代码更稳健案例

不进行异常处理会使得程序遇到异常立即死亡,而进行了异常处理,程序则不会死亡,异常处理会使得代码更稳健。

需求:

键盘录入一个合理的价格为止(必须是数值,值必须大于0)。

分析:

定义一个死循环,让用户不断的输入价格。

代码:

public static void main(String[] args) {
        Scanner sc  = new Scanner(System.in);
        while (true) {
            try {
                System.out.println("请您输入合法的价格:");
                String priceStr = sc.nextLine();
                // 转换成double类型的价格
                double price = Double.valueOf(priceStr);

                // 判断价格是否大于 0
                if(price > 0) {
                    System.out.println("定价:" + price);
                    break;
                }else {
                    System.out.println("价格必须是正数~~~");
                }
            } catch (Exception e) {
                System.out.println("用户输入的数据有毛病,请您输入合法的数值,建议为正数~~");
            }
        }
    }

测试:

在这里插入图片描述

7.自定义异常

7.1.自定义异常的必要

Java无法为这个世界上全部的问题提供异常类,因为Java不知道你的业务逻辑所定义的异常是什么,如果企业想通过异常的方式来管理自己的某个业务问题,就需要自定义异常类了。

比如,企业系统认为员工年龄超过200岁就是异常,或者性别是女的就是异常,对于这些问题,明显Java不会提供异常类,需要我们自己自定义异常。

7.2.自定义异常的好处

可以使用异常的机制管理业务问题,如提醒程序员注意(这个位置可能会出现异常)。
同时一旦出现bug,可以用异常的形式清晰的指出出错的地方(在第几行)。

7.3.自定义异常的分类

1、自定义编译时异常

  • 定义一个异常类继承Exception
  • 重写构造器,用于自定义异常信息。
  • 在认为是异常的地方用throw new 自定义对象抛出

编译前就需要处理,throws或try…catch。

自定义编译时异常类:

public class ItheimaAgeIlleagalException extends Exception{
    public ItheimaAgeIlleagalException() {
    }

    public ItheimaAgeIlleagalException(String message) {
        super(message);
    }
}
//抛出ItheimaAgeIlleagalException异常给方法调用者,这里是JVM
    public static void main(String[] args) throws ItheimaAgeIlleagalException {
        int age = 201;

        if (18 <= age &&  age <= 200) {
                System.out.println("年龄合法,开始推荐");
        } else {
            //现在我认为未满18岁或大于200岁是异常
            //抛出ItheimaAgeIlleagalException异常到函数体
            throw  new ItheimaAgeIlleagalException("ItheimaAgeIlleagalException,请输入合法的年龄~");
        }
    }

测试:

在这里插入图片描述

2、自定义运行时异常

  • 定义一个异常类继承RuntimeException
  • 重写构造器
  • 在出现异常的地方用throw new 自定义对象抛出!

编译前不需要处理异常。

自定义编译时异常:

public class ItheimaAgeIlleagalRuntimeException extends RuntimeException{
    public ItheimaAgeIlleagalRuntimeException() {
    }

    public ItheimaAgeIlleagalRuntimeException(String message) {
        super(message);
    }
}
@Test
    public void test() {
        int age = 201;

        if (18 <= age &&  age <= 200) {
            System.out.println("年龄合法,开始推荐");
        } else {
            //现在我认为未满18岁或大于200岁是异常
            //抛出ItheimaAgeIlleagalException异常到函数体
            throw  new ItheimaAgeIlleagalRuntimeException("ItheimaAgeIlleagalRuntimeException,请输入合法的年龄");

        }
    }

测试:

在这里插入图片描述

7.4.throws和throw的区别和联系

throws和throw的区别,首先是出现的位置不同:

throws是定义在方法上的,而throw是使用在函数体内的。

其次是作用的不同:

throws是异常处理机制的一种,将可能出现的异常(不一定出现)抛给方法的调用者。

throw的作用是将已经出现或者说是构建的异常抛到函数体内,并且如果抛出的编译时异常(继承了Exception),就必须使用异常处理机制(throws或try…catch),处理throw出来的异常。

throws和throw的联系:

throw可以搭配throws使用,当throw到函数的异常是编译时异常,就可以使用throws来处理异常。

当这种处理方式是不负责也不满足日常开发,所以一般是使用 throw 加上 try…catch。

执行流程:

在这里插入图片描述

8.总结

1.异常是什么?

异常是程序执行中出现的错误。

2.异常的分类?

  • Errror:系统级别的错误,异常出现后程序本身无法处理,需要重启应用程序。
  • Exception:java.lang包下的异常,异常出现后程序本身就能解决的异常。
  • RuntimeException:运行时异常,源码编译前不需要进行处理的异常。
  • 编译时异常:源码编译前需要进行处理的异常,否则无法通过编译。

3.编译时异常处理机制有几种?

  • throws:将方法的异常抛给方法的调用者,层层往外抛,直至抛给JVM,让JVM杀死程序,异常处之前的代码都会执行,异常处之后的代码不会执行。
  • try…catch:将方法的异常进行捕捉处理,异常处之前的代码和异常处之后的代码都会正常执行。
  • throws和try…catch结合:将需要处理的异常用throws抛给方法调用者,层层往外抛,在外层的方法调用者中统一用try…catch捕捉处理异常。

4.需要注意的点:

异常处在throws和try…catch种的定义不一样,在throws里,异常处就是异常出现的那一行,而在try…catch中,异常处是try里异常出现的那一行及其它往后的代码。

三种异常处理机制的比较:

throws是一种不负责任的行为,层层往外抛,最终把“麻烦”抛给虚拟机,运行完程序异常处之前的代码就杀死程序,异常处往后的代码都不会执行。

try…catch是程序自身处理错误,是一种负责任的行为,而且异常处往后的代码也能继续执行,直至程序执行完毕。但是不好的一点是,当方法嵌套过多时,可能有多个方法需要try…catch,非常麻烦。

如果前提是出现异常的那一行代码会影响其在try里它往后的代码,throws和try…catch相结合这种异常处理方案是最优选择,因为它将所有异常都抛给了最外层调用者去统一处理,能为程序员省去不少时间和精力。

5.异常处理让程序更加稳健

6.自定义异常分类

  • 自定义编译时异常
  • 自定义运行时异常(鸡肋)

7.throw和throws的区别

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值