异常的捕获和处理-程序员宅基地

技术标签: JAVA  java  

认识异常

异常是指程序的运行过程中所发生的不正常事件,如所需文件找不到,网络连接不通或连接中断、算术运算出错(如被零整除)、数组下标越界、装载一个不存在的类、对null对象操作、类型转换异常。异常会中断正在运行的程序。

所有异常类型都是Throwable类的子类,它派生了两个子类:Error类和Exception类。

​ (1).Error类:表示仅靠程序无法恢复的严重错误,如内存溢出、动态链接失败、虚拟机错误,应用程序不应该抛出这种类型的错误(一般由虚拟机抛出)。假如出现这种错误,应尽力使程序安全退出。

​ (2)Exception类:由Java应用程序抛出和处理的非严重错误,如所需文件找不到,网络连接不同通或连接中断,算术运算出错(如被零除)、数组下标越界,装载一个不存在的类,对null对象操作,类型转换异常等。它的各种不同的子类分别对应不同类型的异常,Exception又可分为两大类异常。

​ a.运行异常:包括RuntimeException及其所有子类。不要求程序对他们进行处理,如算术异常ArithmeticException

​ b.Checked异常(非运行时异常):除了运行时异常外的其他从Exception类继承来的的异常

​ 下表列出常见的异常:

异常 说明
Exception 异常层次结构的根类
ArithmeticException 算术错误异常,如以零作为除数
ArrayIndexOutOfBoundsException 数组下标越界异常
NullPointerException 尝试访问null对象成员
ClassNotFoundException 不能加载所需要的类
InputMismatchException 欲得到的数据类型与实际输入的类型不匹配
IllegalArgumentException 方法接收到非法参数
ClassCastException 对象强制类型转换出错
NumberFormatException 数字格式转换异常,如把“abc”转换成数字

异常处理

​ 异常处理机制就像人们对平时可能遇到的意外情况,预先想好了一些处理的办法。在程序执行代码时,若发生了异常,程序会按照预定的处理办法对异常进行处理,异常处理完毕后,程序继续运行(不加异常处理,程序不会继续运行下面的代码)。

​ Java的异常处理是通过5个关键字来实现的,即try、catch、finally、throw和throws。

Try-catch处理异常

Java中提供了try-catch结构进行异常捕获处理,把可能出现异常的代码放入try语句块中,并使用catch语句捕获异常。

​ 快捷键:选中代码,同时按下Ctrl+Alt+t就可以捕获异常。

使用try-catch捕获并处理示例1中的异常

public static void main(String[] args) {
    

        try {
    
            Scanner scanner =new Scanner(System.in);
            System.out.println("请输入被除数");
            int num1 =scanner.nextInt();
            System.out.println("请输入除数:");
            int num2=scanner.nextInt();

            System.out.println(String.format("%d/%d=%d",num1,num2,num1/num2));
            System.out.println("感谢使用本程序");
        } catch (Exception e) {
    
            System.err.println("出现错误:被除数和除数必须是整数,"+"除数不能为零");
            e.printStackTrace();
        }


    }

​ 分析:

​ try-catch语句块的执行流程比较简单,首先执行的是try语句块中的语句,这时可能会出现以下3种情况。

​ (1)如果try语句块中所有语句正常执行完毕,没有发生异常,那么catch语句块中的所有语句都将会被忽略。当在控制台输入两个整数时,示例3中的try语句块中的代码将正常执行,不会执行catch语句块中的代码。输出结果

请输入被除数

200
请输入除数:
4
200/4=50
感谢使用本程序

​ (2)如果try语句块在执行过程中发生异常,并且这个异常与catch语句块中声明所示。的异常类型匹配,那么try语句块中剩下的代码都将被忽略,而相应的catch语句块将会被执行。匹配是指catch所处理的异常类型与所生成的异常类型完全一致或是它的父类。当在控制台提示输出被除数时,输入了"B",示例3中try语句块中的代码:

"int num1 =in.nextInt();"将抛出InputMismatchException异常。由于InputMismathException是Exception的子类,程序将忽略try语句中剩下的代码而去执行catch语句块。输出结果如下

请输入被除数

200
请输入除数:
B
出现错误:被除数和除数必须是整数,除数不能为零
java.util.InputMismatchException

​ 如果输入的除数为0,输出的结果如下

请输入被除数
200
请输入除数:
0
出现错误:被除数和除数必须是整数,除数不能为零
java.lang.ArithmeticException: / by zero

​ (3)如果try语句块在执行过程中发生异常,而抛出的异常在catch语句中没又被声明,那么方法立刻退出

​ 如示例3所示,在catch语句块中可以加入用户自定义处理信息,也可以调用异常对象的方法输出异常信息,常用方法如下:

​ a.void printStackTrace():堆栈信息包括程序运行到当前类的执行流程,它将输出从方法调用处到异常抛出处的方法调用实例。

​ b.String getMessage():返回异常信息描述的字符串,该字符串描述了异常产生的原因,是printStackTrace()输出的信息的一部分。

注意:
​ 如果try语句块在执行过程中发生异常,try语句块中剩下的代码都将被忽略,系统会自动生成相应的异常对象,包括异常的类型、异常出现时程序的运行状态及对该异常的详细描述。如果这个异常对象与catch语句块中声明的异常类型匹配,会把该异常对象赋给catch后面的异常参数,相应的catch语句块将会被执行。

Try-catch-finally处理异常

如果希望示例3中不管是否发生异常,都执行输出”感谢使用本程序!“语句,就需要在try-catch语句块后加入finally语句块,把要执行输出的语句放入finally语句块中,无论是否发生异常,finally语句块中的代码总能被执行。

 public static void main(String[] args) {
    

        try {
    
            Scanner scanner =new Scanner(System.in);
            System.out.println("请输入被除数");
            int num1 =scanner.nextInt();
            System.out.println("请输入除数:");
            int num2=scanner.nextInt();

            System.out.println(String.format("%d/%d=%d",num1,num2,num1/num2));

        } catch (Exception e) {
    
            System.err.println("出现错误:被除数和除数必须是整数,"+"除数不能为零");
            e.printStackTrace();

        }finally {
    
            System.out.println("/by zero");
            System.out.println("感谢使用本程序");
        }


    }

分析:

​ try-catch-finally语句块的执行流程大致分为如下两种情况。
​ (1)如果所语正常执行完毕,finally语句块也会被执行,例如,当在控制台输入两个数字时,示例4中的try语句块中的代码将正常执行,不会执行 catch-fnally代将被执行输出结果如下所示。

请输入被除数
200
请输入除数:
0
/by zero
感谢使用本程序
出现错误:被除数和除数必须是整数,除数不能为零

​ (2)如果异常,无这种异常能否被catch语句块捕获到,将行finally语句中的代码。例,当在控制台输入的除数为0时,示例4中的try语句块中将抛出异常,进入catch句块finally语句块中的代码也将被执行输出结果如图97所示。
​ try-catch-finally结构中try语句块是必须存在的,catch、finally语句块为可选,但两者至少出现其中之一。
需要特别注意的是,即使在catch语句块中存在retun语句,finally语句块中的语句也会执行。发生异常时的执行顺序是,先执行catch块中retun之前的语句,再执行finally语句块中的语句,最后执行catch语句块中的return语句退出。

​ finally语句块中语句不执行的唯一情况是在异常处理代码中执行System.exit(1)退出Java虚拟机。如下示例5

示例

​ 在try-catch-finally结构的catch语句块中执行System.exit(1)退出Java虚拟机

 public static void main(String[] args) {
    


        try {
    
            Scanner scanner =new Scanner(System.in);

            System.out.println("请输入被除数:");

            int num1 =scanner.nextInt();

            System.out.println("请输入除数");

            int num2=scanner.nextInt();

            System.out.println(String.format("%d/%d=%d",num1,num2,num1/num2));
        } catch (Exception e) {
    
            System.err.println("出现错误:被除数和除数必须是整数,"+"除数不能为零");
            System.exit(1); //finally 语句块不执行的唯一情况
            //return;如果用return,finally代码块依旧执行
        } finally {
    
            System.out.println("感谢使用本程序");
        }

    }

使用多重catch处理异常

​ 在计算并,输出商的示例中,至少存在两种异常情况,输入非整数内容和除数为0,在示例4中,统一按照Exception类型捕获,其实可以使用多重catch语句块分别捕获并处理对应异常。

​ 一段代码可能会引发多种类型的异常,这时,可以在一个try语句块后面跟多个catch语句块分别处理不同的异常。但排列顺序必须是从子类到父类,最后一个一般都是Exception类。因为按照匹配原则,如果父类异常放到前面,后面的catch语句块将不会有被执行机会。

​ 运行时,系统从上到下分别对每个catch语句块处理的异常类型进行检测,并执行第一个与异常类型匹配的catch语句。执行其中的一条catch语句后,其后的catch语句将被忽略。

示例

​ 对示例4进行修改

    public static void main(String[] args) {
    


        try {
    
            Scanner scanner =new Scanner(System.in);

            System.out.println("请输入被除数");
            int num1 =scanner.nextInt();
            System.out.println("请输入除数");
            int num2 =scanner.nextInt();

            System.out.println(String.format("%d/%d=%d",num1,num2,num1/num2));
        } catch (InputMismatchException e) {
    
            System.err.println("被除数和除数必须是整数。");
        } catch (ArithmeticException e){
    
            System.err.println("除数不能为0");
        }catch (Exception e){
    
            System.err.println("其他未知的异常");
        }finally {
    
            System.out.println("感谢使用本程序");
        }

    }

​ 程序运行后,如果输入的不是整数,系统会抛出InputMismatchException异常对象,因此进入第一个catch语句块,并执行其中的代码,而其后的catch语句块将被忽略。输出结果如下:

请输入被除数
0.2
感谢使用本程序
被除数和除数必须是整数。

​ 如果系统提示输入被除数时输入“200”,系统会接着提示输入除数,当输入“0”时,系统会抛出ArithmeticException异常对象,因此进入第二个catch语句块并执行其中的代码块,其他的catch语句块将被忽略,如下:

请输入被除数
200
请输入除数
0
感谢使用本程序
除数不能为0

提示:

​ 在使用多重catch语句块时,catch语句块的排列顺序必须是从子类到父类,最后一个一般都是Exception类。下面代码片断是错误的。

​ 提示爆红:Exception ‘java.lang.ArithmeticException’ has already been caught

​ 已捕获异常“java.lang.ArithmeticException”

​ 原因:我们父类异常放在子类的异常前面,然而父类的异常涵盖了子类的异常

示例:

		  catch (Exception e){
            System.err.println("其他未知的异常");
        } catch (ArithmeticException e){ //提示爆红
            System.err.println("除数不能为0");
        }finally {
            System.out.println("感谢使用本程序");
        }

使用throws抛出异常

​ 如果一个方法体中抛出了异常,并希望调用者能够及时的捕获异常,Java语言中通过关键字throws声明某个方法可能抛出的方法中异常以通知调用者。throws可以同时声明多个异常,之间用,号隔开

​ 下面的示例,把计算输出商的任务封装在了divide()方法中,并在方法的参数列表后通过throws声明抛了异常,然后在main()方法就知道divide()方法中抛出了异常,可以采用如下两种方法进行处理

​ 1).通过try-catch捕获并处理·异常

​ 2)通过throws继续声明异常,如果调用者不知道如何处理该异常,可以继续通过throws声明异常,让上一级调用者处理异常。main()方法的异常将由Java虚拟机处理。

示例:

​ 在Java程序中使用throws声明抛出异常

 public static void main(String[] args) {
    


        try {
    
            divide();
        } catch (InputMismatchException e) {
    
            
            System.err.println("被除数和除数必须是整数");
        } catch (ArithmeticException e) {
    
            System.err.println("除数不能为0");
            
        } catch (Exception e) {
    
            e.printStackTrace();
        }finally {
    
            System.out.println("感谢使用本程序!");
        }


    }
    
    //通过throws声明抛出异常
    public static void  divide() throws Exception{
    


        Scanner scanner =new Scanner(System.in);

        System.out.println("请输入被除数");
        int num1 =scanner.nextInt();

        System.out.println("请输入除数");
        int num2 =scanner.nextInt();

        System.out.println(String.format("%d/%d=%d",num1,num2,num1/num2));
    }

使用thow抛出异常

​ 除了系统自动抛出异常外,在编程过程中,有些问题是系统无法自动发现解决的,如果年龄不在正常范围内,性别输入不是“男”或“女”等,此时需要程序而不是系统来自行抛出异常,把问题交给调用者去解决。

​ 在Java语言中,可以使用throw关键字来自行抛出异常。在下面示例9的带码中抛出了异常,抛出异常的原因在于当前环境无法解决参数问题,因此在方法内部通过throw抛出异常,把问题交给调用者解决。

示例:

​ 在Java程序中使用throw抛出异常。

​ 1).先创建一Person类

public class Person {
    

    private String name="";  //姓名
    private int age =0;   //年龄
    private String sex="男";  //性别

    //设置性别
    public void setSex(String sex)throws Exception{
    
        if ("男".equals(sex)||"女".equals(sex)) {
    
            this.sex=sex;
        }else {
    
            throw new Exception("性别必须是\"男\"或者\"女\"!");
        }
    }


    //输出基本信息
    public void  print(){
    
        System.out.println(this.name+"("+this.sex+","+this.age+"岁)");
    }
    
}
	

​ 2).测试

public static void main(String[] args) {
    

        Person person = new Person();

        try {
    
            person.setSex("man");
            person.print();
        } catch (Exception e) {
    
            e.printStackTrace();
        }
    }

​ 3)运行结果:

java.lang.Exception: 性别必须是"男"或者"女"!
	at com.shujia.Person.setSex(Person.java:23)
	at com.shujia.Test8.main(Test8.java:17)

Process finished with exit code 0

注意:

​ throw和throws的区别如下:

​ 1).作用不同:throw用于程序员自行产生并抛出异常,throws用于声明该方法内抛出了异常。

​ 2).使用的位置不同:throw位于方法体内部,可以作为单独语句使用;throws必须跟在方法参数列表的后面,不能单独使用。

​ 3)内容不同:throw抛出一个异常对象,只能是一个;throws后面跟异常类,可以跟多个。

自定义异常

​ 当JDK中的异常类型不满足我门需求,我门就可以自定义异常。

​ 自定义异常:

​ 1.定义异常类,继承Exception或者RuntimeException

​ 2.编写一个异常类并编写一个构造方法,并继承父类的实现。

public Exception() {
    
        super();//调用了父类的无参构造方法
    }



 public Exception(String message) {
    
        super(message);//调用了父类里面带String的参数的构造方法
    }


//现在所有的Throwable的子类构造器中都可以接收一个cause对象作为参数,这这参数就是异常的原由,代表的原始异常,即使在当前位置创建并抛出行的异常,也可以通过这个cause追踪到异常最初发生的位置。
public Exception(String message, Throwable cause) {
    
        super(message, cause);//调用了父类含有cause参数的构造函数
  	
    }


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

异常链

​ 背景: 在异常处理时:A方法调用了B方法,B方法抛出了异常,那么A方法是继续抛出原有的异常还是抛出个新异常

​ 如果抛出原有的异常将是很糟糕的设计方法。因为A方法与B方法进行了关联,不便于代码的修改和扩展,若抛出新的异常,虽然解决了A方法和B方法的关联问题,但是原有的异常信息却会丢失。JDK1.4推出了异常链,解决了这个问题,所有的Throwable的子类在构造器中都可以接收一个cause对象作为参数。这个cause就用来表示原始异常,这样可以把原始异常传递给新的异常,使得即使在当前位置创建并抛出新的异常,你也能通过这个异常链跟踪到异常最初发生的位置。

通俗的来讲:把捕获的异常包装成一个新的异常,在新的异常中添加对新的异常的引用,再把新异常抛出,就像是链式反应一样,这种就叫异常链。

总结:

​ 1.异常就是在程序的运行过程中所发生的异常事件

​ 2.Java处理异常通过5个关键字来实现,,try、catch、finally、throw、throws

​ 3.即使在try语句块,catch语句中存在return语句,finally语句块也会去执行,finally语句块中不执行的原因,唯一原因,在异常处理代码中执行了System.exit(1).跳出了Java虚拟机

​ 4.可以在try语句块后面跟多个catch语句块,分别处理不同的异常,但是排序必须是从·特殊异常到一般异常,最后一个一般是Exception类

​ 5.Java语言通过关键字throws声明某个方法可能抛出的各种异常以通知调用者

​ 6.在Java语言中,通过throw关键字来自行抛出异常

​ 7.自定义异常一般需要继承Execption或者RuntimeException

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_57683680/article/details/125757963

智能推荐

Pycharm Debug调试(纯干货)-程序员宅基地

文章浏览阅读3w次,点赞65次,收藏374次。内容目录(原文见公众号python宝或www.xmmup.com)一、打断点二、代码调试三、界面小图标介绍四、控制台介绍# 数字转换为大写人民币import sysimport io..._pycharm debug

android MediaPlayer + GLSurfaceView播放视频_mediaplayer glsurfaceview-程序员宅基地

文章浏览阅读4.7k次。1、配置layout<?xml version="1.0" encoding="utf-8"?><LinearLayout ="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" >_mediaplayer glsurfaceview

一文掌握大模型提示词技巧:从战略到战术-程序员宅基地

文章浏览阅读5.2k次,点赞76次,收藏106次。本文从战略(宏观)和战术(微观)两个层次讲解提示词技巧。希望大家能够掌握常见的提示词技巧,能够在 AI 早期积极主动学习,占领先机。_大模型提示词

Description Resource Path Location Type The superclass "javax.servlet.http.HttpServlet" was not foun_"descriptionresourcepathlocationtype the superclas-程序员宅基地

文章浏览阅读2.4k次,点赞3次,收藏3次。建了一个简单web项目,但是项目出了一个Description Resource Path Location TypeDescription Resource Path Location TypeThe superclass “javax.servlet.http.HttpServlet” was not found on the Java Build Path index.jsp /s..._"descriptionresourcepathlocationtype the superclass \"javax.servlet.http.h"

Install Sun JDK on Fedora/Redhat[收藏]_fedora install jdk-程序员宅基地

文章浏览阅读1.8k次。1. Download Sun Java JDK or JRE Download Sun Java JDK or JRE from here (current version is JDK 6 Update 20)http://java.sun.com/javase/downloads/index.jsp.Note: you can Skip login step.Download rpm.bin package (example jdk-6u20-linux-i586-rpm.bin).2. Change_fedora install jdk

android 调用拍照 程序崩溃-程序员宅基地

文章浏览阅读108次。_andori拍照返回app 崩溃

随便推点

用opencv的dnn模块做yolov5目标检测_opencv yolov5-程序员宅基地

文章浏览阅读7w次,点赞271次,收藏1.1k次。最近在微信公众号里看到多篇讲解yolov5在openvino部署做目标检测文章,但是没看到过用opencv的dnn模块做yolov5目标检测的。于是,我就想着编写一套用opencv的dnn模块做yolov5目标检测的程序。在编写这套程序时,遇到的bug和解决办法,在这篇文章里讲述一下。在yolov5之前的yolov3和yolov4的官方代码都是基于darknet框架的实现的,因此opencv的dnn模块做目标检测时,读取的是.cfg和.weight文件,那时候编写程序很顺畅,没有遇到bug。但是yolo_opencv yolov5

3的倍数(暴力搜索)_3的倍数csdn-程序员宅基地

文章浏览阅读168次。牛客小白月赛20D 3的倍数题目链接算法分析n最大为15,范围比较小,所以直接来采用爆搜就行算法实现#include<iostream>#include<cstdio>#include<string>#include<cstring>#include<math.h>using namespace std;int ch[20][30];//ch[i][j]记录第i个字符串中j的个数,j为字符转换后的数字int dp[30];/_3的倍数csdn

【腾讯优测干货分享】如何降低App的待机内存(二)——规范测试流程及常见问题...-程序员宅基地

文章浏览阅读71次。本文来自于腾讯优测公众号(wxutest),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/806TiugiSJvFI7fH6eVA5w作者:腾讯TMQ专项测试团队导语最近小优听说,隔壁的腾讯TMQ团队出了一本新书——《移动App性能评测与优化》,便借阅了一本,读完感觉写得确实很赞。这本书体系化地介绍了移动应用性能评测与优化的方方面面,如内存,电量..._如何降低app的待机内存

Texlive2020+Texstudio2.12.22资源,附安装教程和书-程序员宅基地

文章浏览阅读1.6k次,点赞21次,收藏6次。Texlive2020+Texstudio2.12.22资源,附安装教程和刘海洋latex入门使用说明书百度云地址文件截图![\[\]](https://img-blog.csdnimg.cn/20201031105628261.png#pic_center)总结百度云地址链接:https://pan.baidu.com/s/1w4ZdEHvgMBF2uURQmnAxXw提取码:6jga复制这段内容后打开百度网盘手机App,操作更方便哦–来自百度网盘超级会员V1的分享文件截图总结如果链接..

Hamburgers 二分答案_hamburger题解-程序员宅基地

文章浏览阅读5.2k次。题目大意:有一种汉堡,用B、S、C三种原料做成,现在告诉你当前有的B、S、C的个数,到商店买的B、S、C的单价(商店无限供应这三种原料),还有你拥有的钱。问最多能做多少个汉堡。刚开始我还以为是模拟,先把能用的用完,再去买。但是写了半天写不下去了,找了一下题解才发现是二分答案板子题。发现自己对二分还是不是很敏感。AC代码://https://blog.csdn.net/hesorche..._hamburger题解

ubuntu下安装uhd+gnuradio_无法定位软件包 libuhd003-程序员宅基地

文章浏览阅读1.9k次。提示:安装uhd+gnuradio实际上并不难,只是实际安装的时候,作为新手经常会因为缺乏相关知识而踩不少坑,以下是我踩坑安装的一些记录。gnuradio+uhd安装过程ubuntu下安装uhd+gnuradioExample: For UHD 3.9.5:Example: For UHD 3.14.0.0win10下安装ubuntu双系统使用usrpb210ubuntu18.04安装方法有两种,一种是使用已经编译好的二进制码,缺点是版本通常比较旧,但学习usrp也不需要太新的版本,另外,这种_无法定位软件包 libuhd003