java 程序在运行过程中发生错误或异常情况是不可避免的,如果每次错误都由程序员手动控制和处理,其工作量极大,对于程序员来说,如果将过多的精力放在异常的处理上,程序员就可以不用再做其他事情了。
Java语言定义了很多异常类,将运行错误和异常的信息和处理方法封装在了异常类中,帮助程序员检查和控制异常,这样程序员就可以将时间和精力放在业务逻辑的实现上,而这套机制就是java的异常处理机制。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TUM5Y912-1624934375173)(https://i.loli.net/2021/06/08/1BAJkeyouGgtEIV.png)]
Java语言按照错误严重性,从throwale根类衍生出Error和Exception两大派系
Error(错误)
程序在执行过程中所遇到的硬件或操作系统的错误。错误对程序而言是致命的,将导致程序无法运行。常见的错误有内存溢出,jvm虚拟机自身的非正常运行,calss文件没有主方法。程序本生是不能处理错误的,只能依靠外界干预。Error是系统内部的错误,由jvm抛出,交给系统来处理。
Exception(异常)
是程序正常运行中,可以预料的意外情况。比如数据库连接中断,空指针,数组下标越界。异常出现可以导致程序非正常终止,也可以预先检测,被捕获处理掉,使程序继续运行。
EXCEPTION(异常)按照性质,又分为编译异常(可检测)和运行时异常(不可检测)。
编译时异常:
又叫可检查异常,通常时由语法错和环境因素(外部资源)造成的异常。比如输入输出异常IOException,数据库操作SQLException。其特点是,Java语言强制要求捕获和处理所有非运行时异常。通过行为规范,强化程序的健壮性和安全性。
运行时异常:
又叫不检查异常RuntimeException,这些异常一般是由程序逻辑错误引起的,即语义错。比如算术异常,空指针异常NullPointerException,下标越界IndexOutOfBoundsException。运行时异常应该在程序测试期间被暴露出来,由程序员去调试,而避免捕获。
所以,java语言处理运行时错误有三种方式,
一是程序不能处理的错误,
二是程序应该避免而可以不去捕获的运行时异常,
三是必须捕获的非运行时异常。
java默认处理机制:
1 抛出异常
2终止程序
异常处理程序机制:
1抛出异常
2try-catch-finally 捕获和处理异常
当Java程序运行到某个方法发生异常时,产生一个对应异常类对象,包含异常事件类型,发生异常是应用程序的状态,和调用过程等信息,然后抛出,运行系统开始查找有没又匹配异常处理程序,么有,就中断程序,有就将控制权交个程序处理程序,处理异常。
a.关键字:
try: 异常执行代码 catch: 捕获异常 finally: 最终执行 throws: 声明异常 throw: 抛出异常
b.语法一:
try{ 执行代码--可能含有异常 }catch(异常类型 e){ 异常处理相关操作 } //后续操作 //问题:如果执行代码中含有多种异常,需要分别处理,如何操作?
c.语法二:
try{ 执行代码 }catch(异常类型 e){ 异常处理相关操作 }finally{ 最终执行; } //后续处理 //finally中的内容不管程序是否出现异常都需要执行的内容: //eg:IO流需要最终关闭流/数据库连接使用完毕需要关闭连接
(***):不管是否有异常都会执行,即使使用return强制结束也会执行,唯一不执行情况System.exit(status);
d.语法3:
try{ 执行代码 }catch(异常类型1 e1){ 异常处理 }catch(异常类型2 e2){ 异常处理 }finally{ 最终执行代码 }
(***):排列catch 语句的顺序:先子类后父类,发生异常时按顺序逐个匹配,只执行第一个与异常类型匹配的catch语句
e.throws:
1).语法: 修饰符 返回值类型 方法名(参数列表) throws 异常类型{ 执行内容 } 2).处理: 方式一:后续调用方法继续声明,后续继续处理,如果由main声明的异常由java虚拟机处理; 方式二: 后续调用方法直接try...catch处理; 3).注意事项: 前面声明的异常的方法中该异常类型指定必须比后续调用或声明范围小或其相同; 声明异常时可以同时声明多个异常;
g.throw:
1).语法: throw new Exception();---创建一个需要的异常类型对象 2).后续操作: 抛出异常以后需要后续处理异常;
4.1 如何选择异常
从开发经验来看,如果在一个应用中,需要开发一个方法(如某个功能的service方法),这个方法如果中间可能出现异常,那么你需要考虑这个异常出现之后是否调用者可以处理,并且你是否希望调用者进行处理,如果调用者可以处理,并且你也希望调用者进行处理,那么就要抛出受检异常,提醒调用者在使用你的方法时,考虑到如果抛出异常时如果进行处理,相似的,如果在写某个方法时,你认为这是个偶然异常,理论上说,你觉得运行时可能会碰到什么问题,而这些问题也许不是必然发生的,也不需要调用者显示的通过异常来判断业务流程操作的,那么这时就可以使用一个RuntimeException这样的非受检异常.
4.2 什么时候才需要抛异常
首先我们需要了解一个问题,什么时候才需要抛异常?异常的设计是方便给开发者使用的,但不是乱用的,笔者对于什么时候抛异常这个问题也问了很多朋友,能给出准确答案的确实不多。其实这个问题很简单,如果你觉得某些”问题”解决不了了,那么你就可以抛出异常了。比如,你在写一个service,其中在写到某段代码处,你发现可能会产生问题,那么就请抛出异常吧,相信我,你此时抛出异常将是一个最佳时机。
4.3 应该抛出怎样的异常
了解完了什么时候才需要抛出异常后,我们再思考一个问题,真的当我们抛出异常时,我们应该选用怎样的异常呢?究竟是受检异常还是非受检异常呢(RuntimeException)呢?我来举例说明一下这个问题,先从受检异常说起,比如以下案例我们通过I/O进行读取文件时,一旦文件地址编写出现错误,则文件获取出现错误,导致配置文件内容获取失败
//初始化连接参数,从配置文件里获得 public static void init(){ Properties params=new Properties(); String configFile = "database.properties"; InputStream is=BaseDao.class.getClassLoader().getResourceAsStream(configFile); try { params.load(is); } catch (IOException e) { e.printStackTrace(); } driver=params.getProperty("driver"); url=params.getProperty("url"); user=params.getProperty("user"); password=params.getProperty("password"); }
ok,看了以上代码以后,你也许心中有一些想法,原来受检异常可以控制义务逻辑,对,没错,通过受检异常真的可以控制业务逻辑,但是切记不要这样使用,我们应该合理的抛出异常,因为程序本身才是流程,异常的作用仅仅是当你进行不下去的时候找到的一个借口而已,它并不能当成控制程序流程的入口或出口,如果这样使用的话,是在将异常的作用扩大化,这样将会导致代码复杂程度的增加,耦合性会提高,代码可读性降低等问题。那么就一定不要使用这样的异常吗?其实也不是,在真的有这样的需求的时候,我们可以这样使用,只是切记,不要把它真的当成控制流程的工具或手段。那么究竟什么时候才要抛出这样的异常呢?要考虑,如果调用者调用出错后,一定要让调用者对此错误进行处理才可以,满足这样的要求时,我们才会考虑使用受检异常。
4.4 应该选用哪种异常
通过以上的描述和举例,可以总结出一个结论,RuntimeException异常和受检异常之间的区别就是:是否强制要求调用者必须处理此异常,如果强制要求调用者必须进行处理,那么就使用受检异常,否则就选择非受检异常(RuntimeException)。一般来讲,如果没有特殊的要求,我们建议使用RuntimeException异常
们才会考虑使用受检异常。
4.4 应该选用哪种异常
通过以上的描述和举例,可以总结出一个结论,RuntimeException异常和受检异常之间的区别就是:是否强制要求调用者必须处理此异常,如果强制要求调用者必须进行处理,那么就使用受检异常,否则就选择非受检异常(RuntimeException)。一般来讲,如果没有特殊的要求,我们建议使用RuntimeException异常