目录
异常处理
一、异常的概念
●异常:在Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发中的语法错误和逻辑错误不是异常)
●Java程序在执行过程中所发生的异常事件可分为两类:
▷Error:Java虚拟机无法解决的严重问题。
▷Exception:其他因为编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如:
♢空指针访问
♢试图读取不存在的文件
♢网络连接中断
♢数组角标越界
●Exception又可以分为两类,分别是:编译时异常和运行时异常
如下图所示:
●异常体系结构
java.lang.Throwable
|---java.lang.Error:一般不编写针对性的代码进行处理
|---java.lang.Exception:可以进行异常的处理
|---编译时异常(checked)
|---IOException
|---FileNotFoundException
|---ClassNotFoundException
|---运行时异常(unchecked,RuntimeException)
|---NullPointerException
|---ArrayIndexOutOfBoundsException
|---ClassCastException
|---NumberFormatException
|---InputMismatchException
|---ArithmeticException
二、异常处理机制
1、异常的处理:抓抛模型
过程一:“抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象,并将此对象抛出。一旦抛出对象以后,其后的代码就不再执行。
关于异常对象的产生
①系统自动生成的异常对象
②手动的生成一个异常对象,并抛出(throw)
过程二:“抓”:可以理解为异常的处理方式:①try-catch-finally;②throws+异常类型
2、try-catch-finally的使用
try{
//可能出现异常的代码
}catch(异常类型1 变量名1){
//处理异常的方式1
}catch(异常类型2 变量名2){
//处理异常的方式2
}catch(异常类型3 变量名3){
//处理异常的方式3
}
......
finally{
//一定会执行的代码
}
说明:
①finally是可选的;
②使用try将可能出现异常代码包装起来,在执行的过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中匹配;
③一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的try-catch结构(在没有finally的情况下),继续执行其后的代码;
④catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓。catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,报错;
⑤常用的异常对象处理的方式:(1)String getMessage();(2)printStackTrace();
⑥在try结构中声明的变量,再除了try结构以后,就不能再被调用;
⑦try-catch-finally结构是可以嵌套使用的。
try-catch-finally中finally的使用:
①finally是可选的;
②finally中声明的是一定会被执行的代码。即使catch中又出现异常了,try中有return语句,catch中有return语句等情况;
③像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要声明在finally中。
注意点:
(1)使用try-catch-finally处理编译时异常,使得程序在编译时就不再报错,但是运行时仍有可能报错。相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。
(2)开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。针对于编译时异常,我们说一定要考虑异常的处理。
3.throws+异常类型的使用
说明:
①“throw+异常类型”写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后续的代码,就不再执行。
②try-catch-finally与throws之间的对比:
try-catch-finally:真正的将异常给处理掉了;
throws:只是将异常抛给了方法的调用者,并没有真正的将异常处理掉。
③我们在开发中如何选择使用try-catch-finally,还是使用throws?
▷如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常,必须使用try-catch-finally方式处理;
▷执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws的方式进行处理。而执行的方法a可以考虑使用try-catch-finally方式进行处理。
在这里,就可以对Java的“重写知识”进行补充说明:子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型。
三、手动抛出异常
格式如下:
throw new 异常类型
throw与throws两者的区别:
throw表示抛出一个异常类的对象,生成异常对象的过程,声明在方法体内;
throws属于异常处理的一种方式,声明在方法的声明处。
例如:上游排污,下游治污。
四、用户自定义异常类
如何自定义异常类?
1、继承于现有的异常体系结构:RuntimeException、Exception
2、提供全局常量:serialVersionUID
3、提供重载的构造器
对以上四个关于异常处理的理论知识进行编程练习
1.判断程序输出的结果
package com.java.exception;
/**
* @description: 判断程序输出的结果
* @author: Fish_Vast
* @Date: 2021/9/1
* @version: 1.0
*/
public class ReturnExceptionDemo {
public static void main(String[] args) {
try {
methodA();
}catch (Exception e){
System.out.println(e.getMessage());
}
System.out.println("----------------------------");
methodB();
}
static void methodA(){
try {
System.out.println("进入方法A");
throw new RuntimeException("制造异常");
}finally {
System.out.println("使用方法A的finally");
}
}
static void methodB(){
try{
System.out.println("进入方法B");
return;
}finally{
System.out.println("别着急,还需要调用一下方法B的finally");
}
}
}
以上程序在main方法中,(1)首先执行try中的methodA,进methodA方法中的try执行“进入方法A”,然后因为有finally,那么先执行finally中的“使用方法A的finally”,之后再手动抛出一个RuntimeException的对象,返回main方法中执行剩下的catch,使用getMessage()输出“制造异常”。(2)main方法中的try-catch执行完后,继续执行methodB,在methodB中,先执行try“进入方法B”,在return之前,执行finally“别着急,还需要调用一下方法B的finally”。至此,整个程序执行结束。
运行结果如下所示:
2.编写应用程序ReturnExceptionDemo2.java, 接收命令行的两个参数, 要求不能输入负数, 计算两数相除。对(NumberFormatException) 数据类型不一致、(ArrayIndexOutOfBoundsException)缺少命令行参数、(ArithmeticException)除0及(EcDef 自定义的异常)输入负数进行异常处理。
package com.java.exception;
/**
* @description:
* 编写应用程序ReturnExceptionDemo2.java, 接收命令行的两个参数, 要求不能输入负数, 计算两数相除。
* 对数据类型不一致(NumberFormatException) 、缺少命令行参数(ArrayIndexOutOfBoundsException)、
* 除0(ArithmeticException)及输入负数(EcDef 自定义的异常)进行异常处理。
* 提示:
* (1)在主类(ReturnExceptionDemo2)中定义异常方法(ecm)完成两数相除功能。
* (2)在main()方法中使用异常处理语句进行异常处理。
* (3)在程序中, 自定义对应输入负数的异常类(EcDef)。
* (4)运行时接受参数 java ReturnExceptionDemo2 20 10 //args[0]=“20” args[1]=“10”
* (5)Interger类的static方法parseInt(String s)将s转换成对应的int值。
* 如: int a=Interger.parseInt(“314”); //a=314;
* @author: Fish_Vast
* @Date: 2021/9/1
* @version: 1.0
*/
public class ReturnExceptionDemo2 {
public static void main(String[] args) {
try {
int i = Integer.parseInt(args[0]);
int j = Integer.parseInt(args[1]);
int result = ecm(i, j);
System.out.println(result);
}catch (NumberFormatException e){
System.out.println("数据类型不一致");
}catch (ArrayIndexOutOfBoundsException e){
System.out.println("缺少命令行参数");
}catch (ArithmeticException e){
System.out.println("除0");
}catch (EcDef e){
System.out.println(e.getMessage());
}
}
public static int ecm(int i, int j) throws EcDef{
if(i < 0||j < 0){
throw new EcDef("分子或分母为负数");
}
return i/j;
}
}
class EcDef extends Exception{
static final long serialVersionUID = -3387516948L;
public EcDef() {
}
public EcDef(String message) {
super(message);
}
}
(1)缺少命令行参数(ArrayIndexOutOfBoundsException)
在未对命令行参数赋值之前,报缺少命令行参数的异常:(如下所示)
(2)数据类型不一致(NumberFormatException)
定义的为int类型数据,但是输入的数据有float类型的值,则会数据类型不一致的异常:(如下所示)
(3)除0(ArithmeticException)
两者相除,除数不能为0,如果设置为0,则报“除0”的异常:(如下所示)
(4)分子或分母为负数(EcDef 自定义的异常)
两者相除,不能够出现负数,否则报EcDef自定义的异常:(如下所示)
以上便是Java之异常处理的相关知识梳理,编写不易,路过的朋友,如果博客内容对你有所帮助的话,希望能一键三连一下呀,谢谢支持哦!