当前位置: 首页 > news >正文

异常以及异常处理

目录

1 什么是异常

2 异常的分类

2.1 异常体系

2.2 异常分类

3 异常的处理

3.1 抛出异常

3.2 捕获异常

4 异常的处理流程

5 自定义异常


1 什么是异常

在Java中,异常是指在程序执行过程中发生的不正常的行为,会中断程序的正常执行。

在之前的学习中,我们也遇到了一些异常,例如:

        1.算术异常

public static void main(String[] args){// 分母为 0System.out.println(10 / 0);
}// 异常信息
Exception in thread "main" java.lang.ArithmeticException: / by zero

        2.数组越界异常

public static void main(String[] args){// 数组长度为5int[] array = {1,2,3,4,5};// 访问数组下标为 10 System.out.println(array[10]);
}// 异常信息
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 5

        3.空指针异常

public static void main(String[] args){// 数组为空int[] s = null;System.out.println(s.length);
}
// 异常信息
Exception in thread "main" java.lang.NullPointerException: Cannot read the array length because "s" is null

2 异常的分类

2.1 异常体系

Java内部维护了一个异常体系:

其中

  • Throwable:是异常体系中的顶层类,其派生出了两个重要子类:Error和Exception
  • Error:是指Java虚拟机无法解决的严重问题,称为错误。例如:JVM的内部错误,资源耗尽等,一旦发生就不能通过简单的代码进行处理。
  • Exception:就是异常,在异常发生后,程序员可以通过代码进行处理,使得程序正常执行。

2.2 异常分类

        上面提到的异常又可以分为两种,一种是编译的时候出现的异常,称为编译时异常,也称受查异常;另一种是程序运行时发生的异常,称为运行时异常,也称非受查异常。

1.受查异常:

  • 受查异常是继承java.lang.Exception类,但是不包括java.lang.RuntimeException类的子类。
  • 受查异常要求必须处理(要么使用try-catch捕获,要么使用throws声明)。
  • 受查异常通常是由外部错误条件引起,这些条件在程序运行时可能经常发生,程序员预见这些异常情况并且在编译阶段就处理它们。

2.非受查异常:

  • 非受查异常包括java.lang.RuntimeException的所有子类,RuntimeException是哪些可能在Java虚拟机正常操作期间抛出的异常的父类。
  • 非受查异常不要求强制处理,它们要么是由编程引起的错误,要么是程序应该在运行时处理的异常。

注意:编译时出现的语法错误,不能称为异常。例如将System.out.println拼写错了,此时编译过程中就会出错,但是这不属于异常;运行时指的是程序已经编译通过得到class文件了,再由JVM执行过程中出现的错误。

3 异常的处理

在Java中处理异常最常用的两种方法就是抛出异常和捕获异常。

3.1 抛出异常

在编写程序时,如果程序中出现错误,此时就需要将错误的信息告知使用者,在Java中,可以借助throw关键字,进行抛出异常。

throw只是抛出异常,并没有进行处理。通俗来说就是,我告诉你了,这里有一个异常,你可以处理或者不处理都和我没有关系,反正我已经抛出了。

语法:

throw new 异常类型("异常产生的原因/要告知使用者的信息");

示例:

// 一个读取数组 array 中下标为 index 值的方法
public static int getKey(int[] array,int index){if(array == null){throw new NullPointerException("数组为空");}if(index < 0 || index > array.length - 1){throw new ArrayIndexOutOfBoundsException("下标越界");}return array[index];
}

注意:

  • throw 必须写在方法体内部
  • 抛出的异常必须是Exception或者Exception的子类
  • 如果抛出的是RuntimeExcetion及其子类,就不需要处理,交给JVM处理即可
  • 如果抛出的是受查异常,程序员必须进行处理,否则无法通过编译
  • 异常一旦抛出,后面的代码就不会继续执行

3.2 捕获异常

捕获异常的方法有两种,一是在方法的参数列表之后使用throws进行声明,二是使用try-catch进行捕获。

1.throws声明

语法:

修饰符 返回值类型 方法名(参数列表) throws 异常类型;

throws只是声明这个方法中可能会出现某个异常,但是真的出不出现也不确定,也就是说在平时的方法中,我们可以在方法后面加上 throws ,但是如果方法中真的出现了这种异常,那么就要求必须在方法的参数列表之后进行 throws 的异常声明。

注意:

  • throws 必须跟在方法的参数列表后面
  • 声明的异常必须是Exception及其子类
  • 方法内部如果抛出了多个异常,那么throws后也要跟多个异常,之间用逗号隔开,如果这些异常有父子关系,那么可以只声明父类的异常
  • 调用声明抛出异常的方法时,调用者必须对异常进行处理,或者继续使用 throws 声明

2.try-catch捕获并处理

前面的throws其实并没有对异常进行真正的处理,只是将异常进行声明,真正要对异常处理,就需要使用try-catch。

语法:

try{// 可能出现异常的代码
}
catch(出现异常的类型 e){// 如果 try 中的代码出现异常,此处catch的异常类型正好是该异常或者该异常的父类// 那么就会捕获到这个异常// 然后对异常进行处理,处理之后跳出catch继续执行后续代码
}
finally{// 无论如何都会执行的代码
}

注意:

  • try块中抛出异常之后的代码不会继续执行
  • 如果抛出的异常类型和catch中的异常类型不匹配,即异常没有成功捕获,那么也不会对这个异常进行处理,相当于没有对这个异常进行任何的处理
  • try块中的代码可能会出现多个不同的异常类型,这个时候需要使用多个catch块进行异常的捕获

如下:

try{// 异常1// 异常2// 异常3
}
catch(异常1的异常类型 e1){// 处理异常1的代码
}
catch(异常2的异常类型 e2){// 处理异常2的代码
}
catch(异常3的异常类型 e3){// 处理异常3的代码
}
finally{//一定执行的代码
}

如果这几个异常处理的方式一样,也可以写成

try{// 异常1// 异常2// 异常3
}
catch(异常1的异常类型 | 异常2的异常类型 | 异常3的异常类型 e){// 处理异常的代码
}
finally{//一定执行的代码
}

如果这几个异常之间存在父子关系,那一定要先写子类的catch,在写父类的catch

  • 可以通过一个catch直接捕获所有异常,即使用Exception(不推荐,因为这样不便于看出抛出的是哪个异常)

3.finally

在写程序的时候,有些程序中会有一些不管怎么样都要执行的代码,这部分代码我们就可以写在finally块中,比如在程序中打开的资源等。

但是,事实是如果try-catch中捕获了异常并且正常进行了处理,那么try-catch-finally后的代码也会执行,那么finally存在的意义又是什么?下面我们通过一个例子来说明:

public static void main(String[] args){int a = getValue();System.out.println(a);
}
public static int getValue(){Scanner scanner = null;try{scanner = new Scanner(System.in);int a = scanner.nextInt();return a;}catch (InputMismatchException e){e.printStackTrace();}finally {System.out.println("finally中的代码...");}System.out.println("try-catch-finally后的代码...");if(scanner != null){scanner.close();}return 0;
}
// 运行结果
10
finally中的代码...
10

从上面的例子中可以看出,其中并没有执行try-catch-finally之后的代码,这就使得没有关闭scanner这个资源。

而finally的执行是在方法返回之前(try或者catch中有return时,finally也会在这个return之前执行),但是如果finally中也讯在return语句,那么就会执行finally中的人return(一般不会在finally中写return语句),所以finally中一般就会写一些资源释放的代码。

4 异常的处理流程

在处理异常的时候,如果没有找到合适的处理方式,就会沿着栈向上传递(其实就是,在方法a中,我们调用了方法b,但是进行异常处理时,调用方法b的这条语句本身没有发生异常,所以就会向上传递,在方法a中寻找异常并进行处理)。

示例:

public static void main(String[] args) {try{func();}catch(ArrayIndexOutOfBoundsException e){e.printStackTrace();}System.out.println("try-catch之后的代码...");
}
public static int func(){int array[] = {1,2,3};return array[3];
}// 运行结果
java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3at exception.Demo2.func(Demo2.java:14)at exception.Demo2.main(Demo2.java:6)
try-catch之后的代码...

如果一直向上传递也没有找到合适的方法处理异常,那么最终就会交给JVM进行处理,也就是和我们没有写try-catch之前一样了。

public static void main(String[] args) {func();System.out.println("try-catch之后的代码...");
}
public static int func(){int array[] = {1,2,3};return array[3];
}// 运行结果 
// 此时 System.out.println("try-catch之后的代码...") 就不会执行
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3at exception.Demo2.func(Demo2.java:18)at exception.Demo2.main(Demo2.java:5)

异常处理的流程

  1. 程序先执行try中的代码
  2. 如果try中的代码出现异常,就会结束try中的代码,在catch中寻找异常类型是否匹配
  3. 如果找到匹配的catch,就执行该catch中的代码
  4. 如果没有找到匹配的catch,那就向上传递
  5. 无论有没有找到与该异常匹配的catch,finally中的代码都会在该方法结束之前执行
  6. 一直向上传递直到main方法中也没有找到与之匹配的异常类型的话,就交给JVM来进行处理,此时,程序也会因为异常中断

5 自定义异常

在开发中,有时候Java中已经内置的异常类不够我们使用,所以我们就需要自己进行定义。

示例:

实现一个登录系统的时候,当用户名或者密码输入错误的时候,我们需要抛出异常并且进行处理

public class UserLogin {private String userName = "admin";private String password = "123456";//定义一个密码异常类,属于受查异常,即编译时出现的异常class PasswordException extends Exception{public PasswordException(String msg){super(msg);}}//定义一个用户名异常类,属于非受查异常,即运行时出现的异常class UserNameException extends RuntimeException{public UserNameException(String msg){super(msg);}}public static void main(String[] args) {UserLogin userLogin = new UserLogin();// 进行异常的处理try{userLogin.login("admin","123456");}catch(PasswordException e){//只需要捕获受查异常即可e.printStackTrace();}}public void login(String userName,String password) throws PasswordException{if(!this.userName.equals(userName)){// 抛出但是不进行处理 受查异常throw new UserNameException("用户名错误!");}if(!this.password.equals(password)){throw new PasswordException("密码错误!");}}
}

注意:

  • 自定义异常可以自己选择继承Exception还是RuntimeException
  • 继承Exception的异常是受查异常,需要进行处理
  • 继承RuntimeExcetion的异常是非受查异常,可以进行处理,也可以不处理
http://www.dtcms.com/a/398889.html

相关文章:

  • 2025年国际知名品牌OMS订单管理系统选型指南:从产品架构,生态资源到成功项目交付案例解析|商派
  • 从传统CNN到ResNet:深度学习中的深层网络革命
  • RAG知识增强系统2 - 检索器retriever
  • 52Hz——FreeRTOS学习笔记——任务的创建
  • 百度权重排名高的网站如何用ps做网站效果图
  • 动态设计网站p2p理财网站开发要求
  • 【AI】【Java后端】RAG 实战示例:SpringBoot + 向量检索 + LLM 问答系统
  • Google Pixel 10 vs iPhone 17
  • 2种方式从springbean中获取bean实例
  • iPhone 无线充电发展历史
  • 做康复医院网站推广普通话手抄报
  • Win版 Visual Studio Code配置C++环境
  • 住房与住房建设部网站中美最新军事新闻最新消息
  • uniapp 项目打包时提示未添加videoplayer模块
  • 深入理解Roo Code中的Temperature参数
  • 四、PyTorch训练分类器教程:小张的CIFAR-10实战之旅
  • Unity-序列帧动画
  • 【每日一问】容性负载和感性负载有什么区别?
  • 做汽车保养的网站上企业信息的网站
  • 4-3〔O҉S҉C҉P҉ ◈ 研记〕❘ WEB应用攻击▸文件包含漏洞-A
  • 郑州网站建设国奥大厦南昌营销网站建设
  • 微服务项目->在线oj系统(Java-Spring)----7.0
  • Ant Design Vue Vue3 table 表头筛选重置不清空Bug
  • 【踩坑记录】PyTorch 被误装 CPU 版本导致 CUDA 丢失的解决办法(Windows + Anaconda)
  • 5个问题,帮你选择合适的API测试工具
  • 唐山做网站公司费用郑州做网站哪家好熊掌号
  • 为什么齐次线性方程组的系数行列式为零时有非零解?
  • Cursor Agent模式下面在指定的conda虚拟环境中执行python脚本
  • 福州网站建设加推广怎样把网站打包做百度小程序
  • 元宇宙的工业应用:数字工厂与智能制造