Java异常

本文深入讲解Java中的异常处理机制,包括异常的概念、异常与错误的区别、异常处理的关键字使用、try-catch-finally语句的执行流程、throw和throws的用法、自定义异常的创建及异常链的形成。
摘要由CSDN通过智能技术生成

异常介绍

什么是异常?
       在Java程序编写中,我们经常会遇到编译期间和运行期间的错误,比如括号没有正常的配对,语句少写了分号,关键字编写错误等就是编译期间会出现的错误。通常这些编译错误编译器会帮助我们进行修订。运行期间的错误,比如使用空的对象引用调用方法、数组访问时下标越界、算数运算除数为0、类型转换时无法正常转型等,这些错误在编译的时候完全没有提示。

在程序运行过程中,意外发生的情况,背离我们程序本身的意图的表现,都可以理解为异常。

出现异常的原因:

• 用户不正当的输入

• 本身系统代码编写的问题

    在出现异常的地方,程序会被「异常指令流」直接终止,不再往下运行。为了让程序在出现异常后依然可以正常执行,所以我们必须正确处理异常来完善代码的编写。
    在Java中,提供了一种强大的异常处理机制来帮助我们实现异常的处理。

在这里插入图片描述

异常(Exception)与错误(Error)的区别
  • Error是程序无法处理的错误,表示运行应用程序中较为严重的问题,是代码运行时Java虚拟机中出现的问题。这种错误是不可查的,它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。

    对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。
  • Excepetion是程序本身可以处理的异常。异常处理通常针对这种类型异常的作出处理 。RuntimeException是非检查异常,也就是编译器不要求强制处理的异常。程序员可以针对这些异常进行捕获或放任不管。检查异常包括IO异常和SQL异常,编译器要求必须在代码中处理这些异常。

异常处理

    在Java中异常处理机制分为两种:抛出异常和捕获异常。

    抛出异常指的是一个方法中出现错误引发的异常时,方法会创建异常对象,交给运行时系统进行处理。在异常对象中包含异常类型,异常出现时的程序状态等。运行时系统捕获到这个异常后,进入捕获异常环节,运行时系统会找合适的处理器,与抛出异常匹配后进行处理,如果没找到则程序终止。Java规定,对于检查异常必须捕获、或者声明抛出。对于非检查异常(RuntimeException及其子类)和Error及其子类,允许忽略。

    抛出异常、捕获异常通过5个关键字实现:try、catch、finally、throw、throws。try、catch、finally通常用来捕获异常,throws通常用来声明异常,throw通常用来抛出异常。

在这里插入图片描述

try...catch...finally
try {
    //用于捕获异常
    可能会发生异常的代码块;
} catch(Exception e) {//异常类型
    //用于处理try中捕获的异常
} finally {
    //无论是否发生异常都会执行的代码    
}

    注:try块后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。 也就是说try必须和catch,finally组合使用。catch、finally也不允许单独存在。

public class TryDemo {
    public static void main(String[] args) {
        //定义两个整数,输出两个数的商
        Scanner input = new Scanner(System.in);
        System.out.println("======运算开始=======");
        try{
            System.out.println("请输入第一个整数");
            int one = input.nextInt();
            System.out.println("请输入第二个整数");
            int two = input.nextInt();
            System.out.println("one和two的商是:" + one / two);
        }catch(Exception e) {//这是对异常种类的猜测,通常可以采用异常的父类,就不用猜测具体是哪个子类异常了
            e.printStackTrace();//在控制台打印出异常种类,错误信息和出错位置等,输出位置比较随机
            System.out.println("程序出错了~~~");
        } finally {
            System.out.println("运算结束");
        }
    }
}

    在Java中,可以使用多重catch语句块,针对于同一块代码可能发生的不同种类异常作出处理。比如上述代码可能会发生算术异常也可能会发生其他异常。但是两个catch块中对于异常的猜测是不允许相同的,而且对于父类的异常是必须要放置在子类的异常catch代码块下面的。兄弟异常的catch代码块位置可以随意。

public class TryDemo {
    public static void main(String[] args) {
        //定义两个整数,输出两个数的商
        Scanner input = new Scanner(System.in);
        System.out.println("======运算开始=======");
        try{
            System.out.println("请输入第一个整数");
            int one = input.nextInt();
            System.out.println("请输入第二个整数");
            int two = input.nextInt();
            System.out.println("one和two的商是:" + one / two);
        } catch(InputMismatchException a) {//输入格式异常
            System.out.println("请输入整数!");
            a.printStackTrace();
        } catch(ArithmeticException b) {//数学运算异常
            System.out.println("除数不能为0!");
            b.printStackTrace();
        } catch(Exception e) {//这是对异常种类的猜测,通常可以采用异常的父类,就不用猜测具体是哪个子类异常了
            e.printStackTrace();//在控制台打印出异常种类,错误信息和出错位置等,输出位置比较随机
            System.out.println("程序出错了~~~");
        } finally {
            System.out.println("运算结束");
        }
    }
}

我们会发现,最终输出的异常信息,是我们猜测的某一个异常。同样我们不可能猜测出所有的异常信息,所以需要在catch代码块的最下面写出一个父类异常catch代码块,以免发生遗漏!

终止finally执行的方法
在try、catch、finally语句中,我们会发现无论如何finally语句块中的内容都会去执行,那么有没有一种方法可以终止finally中方法的运行呢?
public class TryDemo {
    public static void main(String[] args) {
        //定义两个整数,输出两个数的商
        Scanner input = new Scanner(System.in);
        System.out.println("======运算开始=======");
        try{
            System.out.println("请输入第一个整数");
            int one = input.nextInt();
            System.out.println("请输入第二个整数");
            int two = input.nextInt();
            System.out.println("one和two的商是:" + one / two);
        } catch(InputMismatchException a) {//输入格式异常
            System.exit(1);//终止程序运行
            System.out.println("请输入整数!");
            a.printStackTrace();
        } catch(ArithmeticException b) {//数学运算异常
            System.out.println("除数不能为0!");
            b.printStackTrace();
        } catch(Exception e) {//这是对异常种类的猜测,通常可以采用异常的父类,就不用猜测具体是哪个子类异常了
            e.printStackTrace();//在控制台打印出异常种类,错误信息和出错位置等,输出位置比较随机
            System.out.println("程序出错了~~~");
        } finally {
            System.out.println("运算结束");
        }
    }
}

System中存在exit方法,该方法的作用可以用来终止Java虚拟机的运行,传入的参数是数字,这个数字只要不是数字0描述的都是异常终止状态。可以使用该方法来使当前程序无条件终止运行。

return关键字在异常处理中的作用
return可以用来提供方法的返回值。那么return是否可以用在异常处理中呢,或者又有什么作用呢
public class TryTest {
    public static void main(String[] args) {
        int result = test();
        System.out.println("运行的结果为:" + result);
    }
    
    public static int test() {
        Scanner input = new Scanner(System.in);
        System.out.println("======运算开始=======");
        try{
            System.out.println("请输入第一个整数");
            int one = input.nextInt();
            System.out.println("请输入第二个整数");
            int two = input.nextInt();
            System.out.println("one和two的商是:" + one / two);
            return one / two;
        } catch(ArithmeticException b) {
            System.out.println("除数不能为0!");
            b.printStackTrace();
            return 0;
        } finally {
            System.out.println("运算结束");
            return -10000;
        }
    }
}

说明:当try,catch,finally三个语句块中都存在return关键字时,无论程序运行正常不正常,最终的结果都是-10000;原因在于finally无论如何都会执行。

throw和throws
使用throws声明异常类型

    可以通过throws声明将要抛出何种类型的异常,如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws子句来声明抛出异常。谁调用这个方法则谁处理抛出的异常。
    throws 后面可以跟多个异常类型,多个异常使用逗号隔开。当方法抛出异常时,方法将不对这些类型及其子类类型异常做处理,而抛向调用该方法的方法,由他去处理。

public class TryTest {
    public static void main(String[] args) {
        try {
            int result = test();
            System.out.println("运行的结果为:" + result);
        } catch(ArithmeticException e) {
            System.out.println("要求除数不能为0");
            e.printStackTrace();
        } catch(Exception e) {
            e.printStackTrace();
        }
        //test();写exception异常后会有提示
    }
    
    public static int test() throws ArithmeticException, Exception{//告诉编译器该方法可能会发生何种异常,可以使用多异常或写Exception
        Scanner input = new Scanner(System.in);
        System.out.println("======运算开始=======");
        System.out.println("请输入第一个整数");
        int one = input.nextInt();
        System.out.println("请输入第二个整数");
        int two = input.nextInt();
        System.out.println("one和two的商是:" + one / two);
        System.out.println("运算结束");
        return one / two;
    }
}

    如果方法抛出的是非检查异常,那么调用此方法处编译器不会强制要求进行异常处理(不会报错),如果方法抛出的异常是检查异常或者Exception,则调用此方法处编译器会强制要求进行异常处理(不处理会报错)。针对不报错的情况,可以在抛出异常的方法处加文档注释,这样调用此方法处虽然编译器不会提示要异常处理,但是文档注释会提示。

throw抛出异常对象

注意,throw抛出的是异常对象。throw抛出的只能是Throwable或者是其子类的实例对象。

用throws往上抛
public class TryTest {
    //酒店入住规则:18以下和80岁以上必须有亲友陪同,不得单独入住
    public static void testAge() throws Exception {
        System.out.println("请输入年龄:");
        Scanner input = new Scanner(System.in);
        int age = input.nextInt();
        if(age < 18 || age > 80) {
            throw new Exception("18岁以下,80岁以上的住客必须由亲友陪同入住");
        } else {
            System.out.println("欢迎入住本酒店!");
        }
    }
    
}
自己抛出自己处理
public class TryTest {
    //酒店入住规则:18以下和80岁以上必须有亲友陪同,不得单独入住
    public static void testAge() {
        System.out.println("请输入年龄:");
        Scanner input = new Scanner(System.in);
        int age = input.nextInt();
        if(age < 18 || age > 80) {
            try {
                throw new Exception("18岁以下,80岁以上的住客必须由亲友陪同入住");
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            System.out.println("欢迎入住本酒店!");
        }
    }
}

throw抛出异常对象的处理方案:

  1. 自己抛出自己处理,通过try-catch包含throw语句。
  2. 用throws往上抛,调用者可以try-catch处理或者继续往上抛。throws抛出异常类型时,要抛出与throw对象相同的类型或者其父类,但不能是子类

注意:throw手动抛出的异常不建议使用非检查类型,因为编译器不提示

自定义异常

    使用Java内置的异常类可以描述在编程时出现的大部分异常情况。也可以通过自定义异常描述特定业务产生的异常类型。所谓自定义异常,就是定义一个类,去继承Throwable类或者它的子类。

public class HotelAgeException extends Exception{
    public HotelAgeException (){
        super("18岁以下住店必须由亲友陪同");
    }
}

补充:getMessage()方法可以显示异常信息。

异常链
public class TryClient {
    
    public static void main(String[] args) {
        try {
            testThree();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void testOne() throws HotelAgeException {
        throw new HotelAgeException();
    }
    
    public static void testTwo() throws Exception {
        try {
            testOne();
        } catch(HotelAgeException e) {
            throw new Exception("我是新产生的异常1",e);//java中保留异常的机制
        }
    }
    
    public static void testThree() throws Exception {
        try {
            testTwo();
        } catch(Exception e) {
            //throw new Exception("我是新产生的异常2",e);或写成另一种方式
            Exception e1 = new Exception("我是新产生的异常2");
            e1.initCause(e);
            throw e1;
        }
    }
}

异常链就是一个异常接着一个异常。最后输出的异常就只有最后一个。

如果想要把前面所有异常都输出的话,则需要通过保留异常的方法:

  1. 通过构造方法对旧异常对象的获取
    Throwable(String message, Throwable cause) – 保留底层异常的异常信息。
  2. 通过initCause(Throwable cause)方法(一个异常的信息来初始化一个新的异常)用来获取原始异常的描述信息,其中cause是原始异常的对象
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值