• Java异常处理(详解)



    前言

    如同大多数现在现代编程一样,Java语言有着健壮的异常处理机制,将控制权从出错点转移给强壮的错误处理器,这篇文章为大家主要讲解Java的异常处理机制。


    一、异常与异常类

    1.异常的概念

    所谓异常是在程序运行过程中产生的使程序终止正常运行的错误对象,如数组下标越界,整数除法中零作除数,文件等找不到等都可能使程序终止运行。

    我们看下面的一段程序

    package com.demo
    public class InputChar{
    	public static void main(String[] args){
    		System.out.print("请输入一个字符:");
    		char c= (char) =System.in.read(); 		//改行发生编译错误
    		System,out.println("c= "+c);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    当编译该程序时会出现下列编译错误:

    Unhandled exception type IOException
    
    • 1

    错误原因是程序没有处理IOException异常,该异常必须捕获或声明抛出。出现编译错误的原因是,read()方法在定义的时候声明抛出了IOException异常,因此程序中调用该方法必须对该类作异常处理



    2.异常类

    java语言的异常处理定义了多种类,其中所有异常类都是Throwable的子类,是Object的直接子类,Throwable有两个子类,一个是Error类,另一个是Exception类,这两个子类又分为若干个子类

    下图展示了Throwable类及其常见子类的层次结构

    在这里插入图片描述



    Error类

    Error类描述的是系统内部错误,这样的错误很少出现,如果发生了这类错误,则除了通知用户及终止程序外,几乎什么也不能做,程序中一般不对这类错误处理



    Exception类

    (1)非检查异常

    非检查异常是RuntimeException类及其子类异常,也称为运行时异常。

    常见的非检查异常如下图所示:

    在这里插入图片描述

    非检查异常是在程序运行时检测到的,可能发生在程序的任何地方且数量较大,因此编译器不对非检查异常(包括Error类的子类)处理,这种异常又称为免检异常


    程序运行时发生非检查异常时运行时系统会把异常对象交给默认的异常处理程序,在控制台显示异常的内容及其发生异常的位置:

    下面介绍几种常见的非检查异常:

    1. NullPointerException:空指针异常,即某个对象为null时调用该对象的方法或使用对象就会产生该异常
    2. ArithmeticException:算术异常,在做整数的除法或者整数的求余运算时可能产生的异常,它是在除数为0时产生的异常
    3. ClassCastException:对象转换异常,Java支持对象类型转换,若不符合转换的规定,则产生类转换异常
    4. ArrayIndexOutOfBoundsException:数组下标越界异常,当引用数组元素的下标超出范围时产生的异常。
    5. NumberFormatException:数字格式错误异常,在将字符串转换为数值时,如果字符串不能正确转换成数值则产生该异常

    注意:尽管编译器不对非检查异常处理,但程序运行时产生这类异常,程序也不能正常结束,为了保证程序正常运行,要么避免产生非检查异常,要么对非异常检查进行处理



    (2)检查异常

    检查异常是除RuntimeException类及其子类以外的异常类,有时候也成为必检异常,对这类异常,程序必须捕获或者声明抛出,否则编译器不会通过。上图中的read()方法声明抛出的IOException异常就是必检异常。例如,若试图使用Java命令运行一个不存在的类,则会产生ClassNotFoundException异常,若调用了一个不存在的方法,则会产生NoSuchMethodException
    异常



    二、异常处理

    1.异常的抛出与捕获

    异常是在方法中产生的,方法中如果在运行过程中产生了异常,在这个方法中就产生一个代表该异常类的对象,并把它交付给系统,运行时系统寻找相应的代码来处理该异常,这个过程称为抛出异常

    关于main()方法中调用情况如下图所示:

    在这里插入图片描述

    main()方法调用methodA()方法,methodA()调用methodB()方法,methodB()调用methodC()方法。
    假如在methodC()方法中发生了异常,运行时首先在该方法中寻找处理异常的代码,如果找不到,运行时系统将在方法调用栈中回溯,把异常对象交给methodB()方法,如果methodB()方法也没有异常处理的代码,将继续回溯,直到找到处理异常的代码,最后,如果main()方法中也没有处理异常的代码,运行时系统将异常交给JVM
    ,JVM将在控制台显示异常信息



    2.try-catch-finally语句

    捕获并处理异常最常用的方法就是用try-catch-finally语句,一般格式为:

    try{
    	//需要处理的代码或者是可能会出现异常的代码
    }catch(ExceptionType1 exceptionObject){
    	//异常处理的代码
    }[catch(ExceptionType1 exceptionObject)]{
    	//异常处理的代码
    }
    finally{
    	//最后处理的代码
    }]
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    说明:
    (1). catch块用来捕获异常,括号中指明捕获的异常类型及其异常引用名,类似于方法的参数,指明catch语句所处理的异常
    注意:若有多个catch块,异常类型的排列顺序必须按照从特殊到一般的顺序,即子类异常放前面,父类异常放在后面,否则会产生编译错误


    (2).finally块是可选项,异常的产生往往会中断应用程序的执行,而在异常产生前,可能有些资源没被释放。有时无论程序是否发生异常,都要执行一段代码,这是就可以用finally块来实现,即使是使用了return语句,finally块都会被执行,除非catch块中调用了System.exit()方法终止了程序的运行

    (3).一个块中必须要有一个catch块或者finally块,即都不能单独使用


    (4).catch块中可以捕获多个异常,例如:
    catch(ExceptionType1 exceptionObject| Exception Type2 exception){ …}


    下图展示用一个try-catch结构捕获并且处理一个ArithmeticException异常

    在这里插入图片描述

    注意在异常类的根类定义了其他方法:如下所示

    public void printStackTrace():在标准错误的输出流上输出异常调用栈的轨迹
    
    public String getMessage():返回异常对象的细节描述
    
    public String toString():返回对象的简短描述,是Object类中同名方法的覆盖
    
    • 1
    • 2
    • 3
    • 4
    • 5


    3.声明方法抛出异常

    有时候方法产生的异常不需要再该方法中处理,可能需要由该方法的调用方法处理,这是可以在声明方法时用throw子句声明抛出异常,将该异常传递给调用该方法的方法处理
    按上述的方式声明的方法,就可对方法中产生的异常不作处理,若方法内抛出了异常,则调用该方法的方法必须捕获这些异常或再声明抛出

    下图为例:

    在这里插入图片描述


    注意:前面讲到子类可以覆盖父类的方法,但若父类的方法使用throws声明抛出了异常,子类方法也可以使用throws方法声明异常。但是要注意,子类方法抛出的异常必须是父类方法抛出的异常或子异常

    下面为例:

    class AA{
    	public void test() throws IOException{
    		System.out.println("In AA's test()")
    	}
    }
    
    class BB extends AA{
    	public void test() throws FileNotFounfException{		//允许,FileNotFounfException是IOException的子类
    		System.out.println("In AA's test()")
    	}
    }
    
    class CC extends AA{
    	public void test() throws Exception{		//错误,Exception是IOException的父类
    		System.out.println("In AA's test()")
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17


    3.用throw 语句抛出异常

    到目前为止,处理的异常都是由程序产生的,并由程序自动抛出,然而也可以创建一个异常对象,然后用throw语句抛出,或将捕获到的异常对象由throw语句再次抛出,throw语句的格式如下

    throw throwableInstance
    
    • 1

    throwableInstance可以是用户创建的异常对象,也可以是程序捕获到的异常对象,该实例必须是Throwable类或者其子类的实例

    下图为例:

    在这里插入图片描述

    注意看图中的序号1,若去掉throw e 这个语句,那么main()主函数中将接受不到方法抛出的异常,因为该方法已经处理了这个异常,大家可以对比这两种图的结果,下图为去掉序号1语句后的结果


    在这里插入图片描述



    三、自定义异常类

    尽管Java已经预定义了许多异常类,但有时还需要定义自己的异常。编写自定义异常类实际上是一个继承API标准异常类,用新定义的异常类处理信息覆盖原有信息的过程。

    常用的编写自定义异常类的模式如下:

    public class CustomException extends Exception {
    	public CustomException(){}
    	public CustomException(){String message) {
    		super(message);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    下面讨论一个例子。假设程序中需要验证用户输入的数据值是否必须是正值,可以按照以上模式编写自定义异常类如下:

    public class CustomException extends Exception {
    	public CustomException(){}
    	public CustomException(){String message) {
    		super(message);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    下图为例:

    在这里插入图片描述



  • 相关阅读:
    【Bluetooth蓝牙开发】六、BLE协议之物理层浅析
    LeetCode155:最小栈,最简单的中等难度题,时间击败100%,内存也低于官方
    vue实现带有横向和纵向双表头
    2024npm国内镜像源
    map-reduce执行过程
    多维时序 | MATLAB实现GWO-GRU灰狼算法优化门控循环单元的多变量时间序列预测
    pikachu——一、暴力破解模块通关教程
    大数据Hadoop之——Hadoop 3.3.4 HA(高可用)原理与实现(QJM)
    守护进程daemon
    2022年PMP考试延迟了,该喜该忧?
  • 原文地址:https://blog.csdn.net/haobabiu/article/details/132909072