Java异常机制:异常体系结构及自定义异常

本文详细介绍了Java的异常处理机制,包括异常的定义、分类(检查性异常、运行时异常和错误)、异常体系结构(Error和Exception)、异常处理(try-catch-finally和throw/throws)以及自定义异常。强调了正确处理异常的重要性,提供了实际应用中的处理建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java异常机制

异常的定义

  1. 在使用计算机语言进行项目开发的时候,即使程序员把代码写的尽善尽美,程序在运行的时候难免会遇到各种突发状况,例如:
    • 你写的某个模块,用户输入的数据格式不一定符合你的要求;
    • 你的程序要打开某个文件,这个文件可能不存在或者格式不对;
    • 你要读取数据库的数据时,数据可能是空的;
    • 我们的程序跑着跑着,内存或者硬盘满了;
  2. 上述等问题都称为异常(Exception),程序报异常能让我们对自己写的程序进行合理的处理,防止程序崩溃。
  3. 异常发生在程序的运行过程中,影响程序的正常运行;

异常的分类

  1. 根据Java的异常处理,简单分为以下三类异常:
    1. 检查性异常:
      • 编译器要求你必须处置的异常:你写的某段代码,编译器要求你必须要对这段代码try…catch,或者throws exception;
      • 最具代表性的检查性异常就是用户错误操作引起的异常;
      • 例如一个文件被用户删除了,然后要打开这个不存在的文件时,一个异常就发生了
      • 这是程序员所无法预见的
    2. 运行时异常
      • 编译器不要求强制处置的异常,与检查性异常相反;
      • 程序员在写程序的时候出现的问题;例如:
        • 除0错误:ArithmeticException;
        • 数组下标越界:ArrayIndexOutOfBoundsException;
        • 使用了空对象NullPointerException等等。
      • 这是可以被程序员避免的异常
    3. 错误
      • 错误其实不是异常,而是脱离程序员控制的问题。
      • 例如当栈溢出了,一个错误就发生了
      • 他们在编译的时候是检查不到的;

异常体系结构

  1. Java把异常当作对象来处理,并定义一个基类Java.lang.Throwable作为所有异常的超类。

  2. 在Java API中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception。

    在这里插入图片描述

Error类

  1. Error类对象由Java虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关;

  2. 大多数是Java虚拟机运行错误(Virtual MachineError):

    • 当JVM不再有继续执行操作所需的内存资源时,将出现OutOfMemoryError。
    • 这些异常发生时,Java虚拟机(JVM)一般会选择线程终止
  3. 还有一种是发生在虚拟机试图执行应用时的错误,这些错误是不可查的,因为他们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。例如:

    • 类定义错误(NoClassDefFoundError)
    • 链接错误(LinkageError)

Exception类

  1. 在Exception分支中有一个重要的子类RuntimeException(运行时异常),这个类的异常会给我们编写的程序定义一些异常,例如:
    • ArrayIndexOutOfBoundsException(数组下标越界异常)
    • NullPointException(空指针异常)
    • ArithmeticException(算数异常)
    • MissingResourceException(丢失资源)
    • ClassNotFoundException(找不到类)
  2. 这些异常都是程序里面已经写好的,不是检查异常,程序中可以选择捕获处理或者不处理。
  3. 发生这些异常一般与我们程序员有关,都是由程序逻辑错误引起,我们要尽量避免这些异常的发生;

Error和Exception的区别

  1. Error通常是灾难性的和致命的错误,这是程序无法控制和处理的;当出现这些错误时,Java虚拟机(JVM)一般会选择终止线程;
  2. Exception通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。

异常处理机制

  1. 异常处理机制一般就两种:
    • 抛出异常
    • 捕获异常
  2. 异常处理的五个关键字:
    • try:监控区域
    • catch:捕获异常
    • finally:处理善后工作
    • throw
    • throws

捕获异常:快捷键Ctrl+Alt+T

  1. 我们通过try…catch…finally…去捕获异常

  2. 第一步try:

    • 捕获异常的第一步是用try{…}语句块选定捕获异常的范围,将可能出现异常的代码放在try语句块中。
  3. 第二步catch:

    • 如果发生异常,则尝试去匹配catch块,catch块可以有多个(因为try块可以出现多个不同类型异常);
    • catch (Exceptiontype e):在catch语句块中是对异常对象进行处理的代码。
    • 每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象。
    • 每一个catch块用于捕获并处理一个特定的异常,或者这异常类型的子类。
    • Java中也可以将多个异常声明在一个catch中。
      • catch(Exception1 | Exception2 | Exception3 e)
      • catch后面的括号定义了异常类型和异常参数。如果异常与之匹配且是最先匹配到的,则虚拟机将使用这个catch块来处理异常。
    • 在catch块中可以使用这个块的异常参数来获取异常的相关信息。异常参数是这个catch块中的局部变量,其它块不能访问。与其它对象一样,可以访问一个异常对象的成员变量或调用它的方法。
      • ①、getMessage() 获取异常信息,返回字符串。
      • ②、printStackTrace() 获取异常类名和异常信息,以及异常出现在程序中的位置。返回值void。
    • 如果当前try块中发生的异常在后续的所有catch中都没捕获到,则先去执行finally,然后到这个函数的外部caller中去匹配异常处理器。
    • 如果try中没有发生异常,则所有的catch块将被忽略。
    • 注意:如果明确知道产生的是何种异常,可以用该异常类作为catch的参数;也可以用其父类作为catch的参数。例如:
      • 可以用 ArithmeticException 类作为参数的地方,就可以用RuntimeException类作为参数,或者用所有异常的父类Exception类作为参数。
      • 但不能是与ArithmeticException类无关的异常,如NullPointerException(catch中的语句将不会执行)。
  4. 第三步finally:

    • 如果执行完try不管有没有发生异常,则接着去执行finally块和finally后面的代码(如果有的话)。
    • finally块通常是可选的,不是一定要写的。
    • 捕获异常的最后一步是通过finally语句为异常处理提供一个统一的出口,使得在控制流转到程序的其它部分以前,能够对程序的状态作统一的管理。
    • 不论在try代码块中是否发生了异常事件,catch语句是否执行,catch语句是否有异常,catch语句中是否有return,finally块中的语句都会被执行。
    • 一个try至少要有一个catch块,否则, 至少要有1个finally块。但是finally不是用来处理异常的,finally不会捕获异常。
    • finally主要做一些清理工作,如IO流的关闭,数据库连接的关闭等。
  5. 注意点:

    • 不要在fianlly中使用return和抛出异常,减轻finally的任务,不要在finally中做一些其它的事情,finally块仅仅用来释放资源是最合适的。
    • 将尽量将所有的return写在函数的最后面,而不是try … catch … finally中。
  6. 下面进行简单的操作:

    package com.exception;
    
    public class Test {
        public static void main(String[] args) {
            int a = 1;
            int b = 0;
            try{//try 监控区域
                System.out.println(a/b);
            }catch(Error e){//catch(想要捕获的异常类型){捕获到异常后的处理}
                System.out.println("Error");
            }catch (Exception e){
                System.out.println("Exception");
            }catch (Throwable e){
                System.out.println("Throwable");
            }finally {
                System.out.println("Finally");
            }
    
            //try{}catch(){}必须要有
            //finally可以不用,一般用于关闭资源流,处理善后工作
        }
    }
    

抛出异常

  1. 抛出异常有两种:throw和throws

  2. 手动抛出异常:throw

    • Java异常类对象除在程序执行过程中出现异常时由系统自动生成并抛出,也可根据需要使用人工创建并抛出。
    • 首先要生成异常类对象,然后通过throw语句实现抛出操作(提交给Java运行环境)。 throw exceptionObject
    • 程序员也可以通过throw语句手动显式的抛出一个异常。throw语句的后面可以抛出的异常必须是Throwable或其子类的实例。
    • 下面的语句在编译时将会产生语法错误:throw new String(“你抛我试试.”);
    • throw 语句必须写在函数中,执行throw 语句的地方就是一个异常抛出点,它和由JRE自动形成的异常抛出点没有任何差别。
  3. throws抛出:

    • throws是另一种处理异常的方式,它不同于try…catch…finally,throws仅仅是将函数中可能出现的异常向调用者声明,而自己则不具体处理。
    • throws声明:如果一个方法内部的代码会抛出检查异常(checked exception),而方法自己又没有完全处理掉或并不能确定如何处理这种异常,则javac保证你必须在方法的签名上使用throws关键字声明这些可能抛出的异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理,否则编译不通过。
    • 在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
    • 采取这种异常处理的原因可能是:方法本身不知道如何处理这样的异常,或者说让调用者处理更好,调用者需要为可能发生的异常负责。
    • 语法格式:
      • 修饰符 返回值类型 方法名() throws ExceptionType1 , ExceptionType2 ,ExceptionTypeN{ //方法内部可以抛出 ExceptionType1 , ExceptionType2 ,ExceptionTypeN 类的异常,或者他们的子类的异常对象。 }
  4. throws和throw的区别:

    • throw是语句抛出一个异常。
      • 语法:throw (异常对象); throw e;
    • throws是方法可能抛出异常的声明。(用在声明方法时,表示该方法可能要抛出异常)
      • 语法:(修饰符)(方法名)([参数列表])[throws(异常类)]{…}
      • public void doA(int a) throws Exception1,Exception3{…}
    • throw语句用在方法体内,表示抛出异常,由方法体内的语句处理。
    • throws出现在方法函数头,表示在抛出异常,由该方法的调用者来处理。
    • throws主要是声明这个方法会抛出这种类型的异常,使它的调用者知道要捕获这个异常。
    • throw是具体向外抛异常的动作,所以它是抛出一个异常实例。
    • throws说明你有那个可能,倾向。
    • throw的话,那就是你把那个倾向变成真实的了。
    • 两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。
  5. 下面进行简单的操作:

    package com.exception;
    
    public class Test2 {
        public static void main(String[] args) {
    
            try {
                new Test2().test(1,0);
            } catch (ArithmeticException e) {
                throw new RuntimeException(e);
            }
        }
    
        //假如这个方法中处理不了这个异常,方法上抛出异常;
        public void test(int a,int b) throws ArithmeticException{
            if (b==0){
                throw new ArithmeticException();//主动的抛出异常,一般在方法中使用
            }
        }
    }
    

自定义异常

  1. 使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自定义异常。用户自定义异常类,只需继承Exception类即可。

  2. 在程序中使用自定义异常类,大体可以分为以下几个步骤:

    1. 创建自定义异常类。
    2. 在方法中通过throw关键字抛出异常对象。
    3. 如果在当前抛出的方法中处理异常,可以使用try-catch语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作。
    4. 在出现异常方法的调用者中捕获并处理异常。
  3. 下面进行简单的操作:

    • 第一步:创建自定义异常类
    public class MyException extends Exception{
        //传递数字
        private int detail;
        public MyException(int a){
            detail = a;
        }
    
        //toString:异常的打印信息
        @Override
        public String toString() {
            return "MyException{" + detail + '}';
        }
    }
    
    • 第二步:在方法中通过throw关键字抛出异常对象1
      • 当输出数字在定义的范围内时:没有异常
    public class Test {
        //可能会存在异常的方法
        static void test(int a) throws MyException {
            System.out.println("传递的参数为:"+a);
            if (a>10){
                throw new MyException(a);//抛出
            }
            System.out.println("OK");
        }
    
        public static void main(String[] args) {
            //try-catch 捕获异常
            try {
                test(10);
            } catch (MyException e) {
                System.out.println("MyException==>"+e);
            }
        }
    }
    

    输出后显示:

    在这里插入图片描述

    • 第二步:在方法中通过throw关键字抛出异常对象2
      • 当输出数字超出定义的范围时:出现异常
    public class Test {
        //可能会存在异常的方法
        static void test(int a) throws MyException {
            System.out.println("传递的参数为:"+a);
            if (a>10){
                throw new MyException(a);//抛出
            }
            System.out.println("OK");
        }
    
        public static void main(String[] args) {
            //try-catch 捕获异常
            try {
                test(11);
            } catch (MyException e) {
                System.out.println("MyException==>"+e);
            }
        }
    }
    

    输出后显示:

    在这里插入图片描述

实际应用中的经验总结

  1. 处理运行时异常,采用逻辑去合理规避同时采用 try-catch 辅助处理。
  2. 在多重catch块后面,可以加一个catch(Exception)来处理可能会被遗漏的异常。
  3. 对于不确定的代码,也可以加上 try-catch ,去处理潜在的异常。
  4. 尽量去处理异常,切忌只是简单地调用 printStackTrace()去打印输出。
  5. 具体如何处理异常,要根据不同的业务需求和异常类型去决定。
  6. 尽量添加finally语句块去释放占用的资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dominator945

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值