JavaSE基础day13异常处理
一.异常
(一)异常的概述
1. 异常概述: 在java程序运行过程中,出现的不正常情况,出现的错误,称为异常。
2. Java中的每一种异常都封装成一个类, 当异常发生时创建出异常对象,对象中包含了异常情况的原因、类型、描述以及位置。
3. 异常也是一种处理异常情况的机制,可以进行跳转、捕获以及结束程序。
(二)异常的体系
1、Throwable:可抛出的,是异常体系的顶层父类,其他的异常或者错误都是Throwable的子类类型,只有Throwable的体系类型,才可以使用异常的处理机制
2、Error:错误,是Throwable的子类,用于描述那些无法捕获和处理的错误情况,属于非常严重的错误,StackOverflowError
3、Exception:异常,是Throwable的子类,用于描述那些可以捕获和处理的例外情况,属于不太严重的错误,ArrayIndexOutOfBoundsException
4、RuntimeException:运行时异常,是Exception的特殊的子类,在编译阶段不做检查的一个异常
5、异常体系结构:
Throwable
Error
Exception
RuntimeException : 运行时异常
除RuntimeException 之外: 编译时期异常
(三)在jvm中默认处理异常的机制
1、在代码的某个位置,出现了和正常情况不同的情况,就将异常情况封装到一个异常对象中。
2、将异常对象抛给调用该方法的方法
3、某个方法接收到底层方法抛上来的异常,也没有办法自己处理,继续向上抛出,最终抛给主方法,主方法也没有办法处理,抛给调用自己的jvm虚拟机
4、jvm虚拟机是我们手动调用的,只能将异常对象的所有信息,通过错误流打印出来,结束jvm虚拟机
5、总结,jvm默认处理的方式:【一层一层向上抛,jvm接收到之后结束自己】
(四)手动处理异常的方式
1、有两大类处理异常的方式:
异常的声明:某个方法有编译时异常,编译就会无法通过,需要在异常所在的方法声明上,声明该方法可能出现的编译时异常
异常的处理:出现异常之后,可以通过某些格式来捕获和处理异常,可以让程序在出现异常之后,继续运行。可以定义自己处理异常的逻辑。
2、捕获处理异常的代码格式:
try...catch
try...catch...finally
try...finally(无法捕获处理异常)
(五)try...catch语句
1、格式:
try {
可能发生异常的代码
} catch(可能出现异常的类型 标识符) {
这种异常出现之后的处理方式
}
2、try:关键字, 试一试
try语句块中,是可能会运行失败的代码,try语句是用于对异常进行检测
3、catch:关键字, 捕获
抓住try语句中出现的异常,并且定义异常处理的方式
小括号中声明可能出现的异常类型,标识符就是一个引用,指向了将来出现的异常对象
大括号中用于定义异常出现之后的处理方式:可以是记录日志、可以是反复运行、可能是把异常封装进新的异常,进行抛出
4、运行机制:
1、运行try语句中的代码
2、如果没有发生任何异常,那么不再运行catch块中的内容
3、如果发生了catch中声明的异常,那么就会被捕获到这个异常,执行catch块中的内容(try中如果发生了异常,try中,该语句后面的代码都无法执行了,直接跳到catch中)
4、如果发生了catch中没有声明的异常,那么就无法捕获该异常,该异常的处理就使用jvm的默认处理方式
public class TryCatch { public static void main(String[] args) { // 1. catch捕获异常成功 /*int i; try{ i = 10 / 0;// 发生了数学运算问题 new ArithmeticException(); // 如果try代码块发生异常, 从发生异常当前行开始, 直接将异常对象抛给catch,try之后代码不再运行 System.out.println(i); }catch(ArithmeticException ex){// ArithmeticException ex = new ArithmeticException(); i = 5; System.out.println("我抓住了异常"); } System.out.println("over");*/ // 2. JVM虚拟机默认处理异常, 因为catch没有捕获异常成功 /*int i; try{ i = 10 / 0; System.out.println(i); }catch(NullPointerException ex){ i = 5; System.out.println("我抓住了异常"); } System.out.println("over");*/ // 3. try中代码没有异常 int i; try{ i = 10 / 1; System.out.println(i); }catch(NullPointerException ex){ i = 5; System.out.println("我抓住了异常"); } System.out.println("over"); } }
(六)try...catch...catch...语句
1、在一段代码中,可能出现多种异常(虽然一次运行只能出现一个异常,但是出现哪个异常我们是不清楚的),所以要准备多种异常情况的处理机制。
2、格式:
try {
可能出现异常的代码
} catch (异常类型1 异常对象名1) {
异常类型1出现之后的处理办法
} catch (异常类型2 异常对象名2) {
异常类型2出现之后的处理办法
}
....
} catch (异常类型n 异常对象名n) {
异常类型n出现之后的处理办法
}
3、执行流程:
1、执行try中的内容,如果没有异常,try...catch语句直接结束
2、如果有异常,那么就在发生异常的代码位置直接跳转到catch块中,try中后面的代码就不再继续运行了
3、继续匹配各个catch块中的异常类型,从上到下,一旦匹配到某个catch声明的异常类型,就直接执行该catch块的处理方式。处理完成之后,try...catch语句就直接结束了,不会再去匹配后面其他的catch块的异常类型
4、注意事项:
如果在各个catch块中,出现了子父类的异常类型,那么子类异常的catch块,必须在父类异常catch块的上面,因为从上到下匹配方式,如果父类的catch块在上面,下面的catch块就没有出现的意义了,无法到达的代码。
import java.io.FileNotFoundException; public class TryCatchCatch { public static void main(String[] args) { /*try{ findFile("abc.txt"); // findFile(null); //findFile("123.txt");// new FileNotFoundException("提供的文件不是abc.txt"); System.out.println("执行完毕"); }catch(FileNotFoundException ex){ // = new FileNotFoundException("提供的文件不是abc.txt"); System.out.println("捕获到了文件找不到异常"); }catch(Exception ex){ System.out.println("捕获了exception异常"); }*/ try{ // findFile("abc.txt"); // findFile(null); findFile("123.txt");// new FileNotFoundException("提供的文件不是abc.txt"); System.out.println("执行完毕"); }catch(Exception ex){// Exception ex = new FileNotFoundException("提供的文件不是abc.txt"); System.out.println("捕获了exception异常"); }/*catch(FileNotFoundException ex){ System.out.println("捕获到了文件找不到异常"); }*/ System.out.println("over"); // 当代码中有多种异常发生: 可以利用多个catch语句依次捕获每一种异常,如果异常之间有子父类的继承关系. // 那么只能先捕获子类异常,后捕获父类异常; 或者直接捕获一个父类异常即可,利用了多态性 } public static void findFile(String str) throws FileNotFoundException, Exception{ if(str == null){ throw new Exception("提供的文件不能为null"); } if(!"abc.txt".equals(str)){ throw new FileNotFoundException("提供的文件不是abc.txt"); } System.out.println(str); } }
(七)try...catch...finally语句
1、格式:
try {
可能发生异常的代码
} catch (可能发生的异常类型 异常对象名称) {
当前异常类型的处理方式
}... finally {
一定要执行的代码
}
2、finally:关键字, 一定要执行的代码
1、如果把某句代码放在try中,可能在这句话前面有异常,那么这句话就无法执行;如果把某句代码放在catch中,有可能try中没有异常,就无法执行这句话;如果把某句代码放在try...catch之后,可能有未捕获的异常,那么这句代码也无法执行。
2、finally:也是一个代码块,在这个代码块中的代码,一定会执行,无论上面描述的哪种情况,都会执行。甚至在代码中有return语句,都会先执行finally中的代码。
3、作用:一般使用关闭资源
public class TryCatchFinally { public static void main(String[] args) { try{ int i = 10 / 1; System.out.println(i); }catch(Exception ex){ System.out.println("异常捕获成功"); }finally{ System.out.println("我一定执行"); } System.out.println("over"); System.out.println(useI());// 88 } public static int useI(){ try{ int i = 10 / 1; return i; }catch(Exception ex){ System.out.println("异常捕获成功"); return 99; }finally{ System.out.println("我一定执行"); return 88; } } }
(八)try...finally
1、格式:
try {
可能发生异常的代码
} finally {
一定要执行的代码
}
2、作用:
1、第三种格式无法捕获和处理异常,一旦发生任何异常,仍然会按照默认的处理方式,一层一层向上抛出,到达jvm,结束虚拟机
2、无论try中的语句是否发生异常,finally中的代码都一定有执行的机会
3、如果有两句代码,都需要有执行的机会,不希望第一句的成功与否影响到第二句的执行机会,那么就把这两句代码分别放在try和finally中
3、使用场景 : 如果代码中发生异常, 不处理, 交给JVM默认处理, 还有一定需要执行逻辑, 那么try...finally可以使用
public class TryFinally { public static void main(String[] args) { try{ int[] arr = new int[5]; System.out.println(arr.length); }finally{ System.out.println("我执行了"); } System.out.println("game over"); } }
(九)运行时异常和编译时期异常的区别
1. 编译时期异常:
a: Exception类和Exception除了RuntimeException以外的其他子类
b: 出现就必须显示的处理,否则程序无法编译通过,程序也就无法运行; 当代码中发生编译时期异常, 提示自动进行处理
2. 运行时异常:
a: RuntimeException类和RuntimeException的子类
b: 出现无需显示处理,也可以像编译时一样显示的处理,无论是否处理程序都可以编译通过。
(十)throw关键字
1、throw:抛出,用于抛出一个异常对象
2、throw关键字的使用场景 : 异常是一个对象,当程序运行到某种情况时,程序员认为这种情况和现实生活不符合,就把当前的对于情况的描述,封装到一个异常对象中,通过throw关键字将异常对象进行抛出。将异常抛给方法的调用者
3、throw关键字的语法结构:
throw new Exception或者Exception任意一个子类对象(“将异常发生详细信息描述清楚”);
4、作用:
创建一个异常对象,使用throw关键字抛出,实现了程序的结束或者跳转
5、说明:
(1) 如果抛出的是编译时异常,那么这个异常必须使用异常处理的方式处理,才能编译成功
(2) 如果抛出的是运行时异常,在编译阶段就相当于没有异常,可以不处理这个异常
6. throw关键字使用注意事项:
(1) throw关键字使用在方法中
(2) throw每一次只能抛出一个异常
public class ThrowDemo { public static void main(String[] args) { int[] arr = null; useArray(arr,1); } // useArray方法获取到arr数组中index索引位置元素 public static void useArray(int[] arr, int index){ if(arr == null){ throw new NullPointerException("数组不能为null"); } if(index < 0 || index >= arr.length-1){ throw new ArrayIndexOutOfBoundsException(index + "在arr中不存在"); } System.out.println(arr[index]); } }
(十一)throws关键字
1、throws:抛出,用于声明异常类型
2、在某个方法中,有一些编译时异常,没有给出处理的方案,没有捕获这个异常,没有处理这个异常,就说明这个方法是一个有问题的方法。为了让调用者在调用时,可以考虑到处理这个异常,所必须在当前方法的声明上,声明这个异常。
3、声明格式:
修饰符 返回值类型 方法名称(参数列表) throws 异常类型1, 异常类型2,... {
可能出现异常的代码;
}
4、注意事项:
(1) 如果抛出的是一个运行时异常,那么就相当于没有抛出异常,这种异常也不需要在方法上声明;声明了一个运行时异常,也相当于没有做任何声明
(2) 如果抛出的是一个编译时异常,那么就必须进行声明或者捕获;如果声明了一个编译时异常,将来调用这个方法时,也相当于有一个声明的异常
mport java.io.FileNotFoundException; public class ThrowsDemo { public static void main(String[] args) throws FileNotFoundException { // String str = "123.docx"; // String str = null; String str = "abc.txt"; findFile(str); } // 验证参数提供的str字符串是否与目标文件abc.txt匹配 // throws关键字: 就是异常的其中一种解决方案, 将编译时期的异常做声明; 效果就是提醒方法的调用者对于可能会发生的编译时期异常进行预判和预处理 public static void findFile(String str) throws FileNotFoundException{ if(str == null){ // 因为NullPointerException是一个运行时异常, 因此不需要在代码中进行处理 throw new NullPointerException("提供的文件不能为null"); } if(!"abc.txt".equals(str)){ // FileNotFoundException是编译时期异常, 发生必须进行异常处理 throw new FileNotFoundException("提供的文件不是abc.txt"); } System.out.println(str); } }
(十二)throw和throws的比较
1、throw是对异常对象的抛出,throws是对异常类型的声明
2、throw是对异常对象实实在在的抛出,一旦使用了throw关键字,就一定有一个异常对象出现;throws是对可能出现的异常类型的声明,即使声明了一些异常类型,在这个方法中,也可以不出现任何异常。
3、throw后面只能跟一个异常对象;throws可以跟很多个异常类型
(十三)异常体系中的常用方法
1、发现在异常的继承体系中,所有的方法定义在了Throwable这个顶层父类中,子类中几乎没有什么特有方法
2、Throwable中的构造方法:
Throwable():创建一个没有任何参数的异常对象
Throwable(String message):创建一个带有指定消息的异常对象
3、常用成员方法:
getMessage():获取异常的详细信息
toString():获取异常对象的详细信息
printStackTrace():打印异常的调用栈轨迹(有关异常的方法调用路径), 对于异常信息追全面的输出方式, 本身返回值结果void, 但是在方法中已经有打印功能, 将异常进行输出,包括异常类型, 异常详细信息, 代码发生异常的行数
import java.io.FileNotFoundException; public class ExceptionMethod { public static void main(String[] args) { try { findFile("123.txt"); } catch (Exception e) { // 1. 获取封装的异常信息:提供的文件不是abc.txt String str = e.getMessage(); System.out.println(str); // 2.获取异常对象信息: java.io.FileNotFoundException: 提供的文件不是abc.txt String str1 = e.toString(); System.out.println(str1); // 3. printStackTrace(): 打印详细的异常堆栈信息 e.printStackTrace(); } } public static void findFile(String str) throws FileNotFoundException,Exception{ if(str == null){ throw new Exception("提供的文件不能为null"); } if(!"abc.txt".equals(str)){ throw new FileNotFoundException("提供的文件不是abc.txt"); } System.out.println(str); } }
(十四)异常的注意事项
1. 运行时异常抛出可以不处理,既不捕获也不声明抛出; 编译时期异常, 必须进行异常处理,throws声明异常, 或者try...catch语法结构捕获处理异常
2. 如果父类抛出了多个编译时异常,子类覆盖(重写)父类的方法时,可以不抛出异常,或者抛出相同的异常,或者父类抛出异常的子类。
3. 如果父类方法没有异常抛出,子类覆盖父类方法时,不能抛出编译时异常
public class Fu { public void fun() throws Exception{ System.out.println("父类fun"); } }
import java.io.FileNotFoundException; public class Zi extends Fu{ /* 子类重写父类中的方法: 要求重写方法异常小于等于父类异常(声明比父类更少的异常,或者声明异常是父类异常的子类) */ @Override public void fun() throws FileNotFoundException{ System.out.println("父类fun"); } }
(十五)自定义异常
1、jdk中提供了很多的异常类型,其中的绝大部分都没有自己特有的方法,都无法描述当前的异常情况,就需要我们自己定义异常类型,用在自己的项目的业务中。
2、自定义异常的步骤:
(1) 定义一个类,以Exception结尾,IllegleAgeException,表示这是一个非法年龄异常
(2) 让自己定义的这个类,继承一个Exception或者是RuntimeException
如果定义的是编译时异常,就使用Exception
如果定义的是运行时异常,就使用RuntimeException
(3) 构造方法不能被继承,需要手动添加
public class ArrayException extends Exception{ public ArrayException(){} public ArrayException(String message){ super(message); } public int[] getArray(int n){ return new int[n]; } }
public class TestArrayException { public static void main(String[] args) { int[] arr = null; try { useArray(arr); } catch (ArrayException e) { arr = e.getArray(6); System.out.println("问题解决"); } System.out.println("over"); } public static void useArray(int[] arr) throws ArrayException { if(arr == null){ throw new ArrayException("传递数组为null"); } System.out.println(arr.length); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能