我们之前见过的异常有:
防御式编程 错误在代码中是客观存在的,因此我们要让程序出现问题的时候及时通知程序猿. 我们有两种主要的方式 1.
举个例子:
2.
举个例子:
虽说有两种方式,但是我们常用EAFP方式异常的基本用法 我们来看一个例子: finally可以省略~~但是如果不省略finally,无论是否存在异常, finally 中的代码一定都会执行到.finally中一般是为了回收资源--->执行到 Scanner 的 close 方法. 说到回收资源,try也可以负责回收资源,这样就可以不用finally了 举个例子: 下面我们再补充一下finally的注意事项:
finally 中的代码保证一定会执行到. 这也会带来一些麻烦.
---->finally 执行的时机是在方法返回之前(try 或者 catch 中如果有 return 会在这个 return 之前执行 finally). 但是如果finally 中也存在 return 语句, 那么就会执行 finally 中的 return, 从而不会执行到 try 中原有的 return.一般我们不建议在 finally 中写 return (被编译器当做一个警告)
举个例子:
关于catch的几点,我们要注意: 1.catch只能处理对应类的异常,解释一下,就比如上面的例子里,try里的语句可能出现越界异常,因此catch中应该放越界异常的类,如果catch中放的是空指针异常,那么catch就捕捉不到越界异常了 2.在一段代码中可搭配多个catch
比如像这样:
如果多个异常的处理方式相同,catch语句也可这样写: 3.catch 进行类型匹配的时候, 不光会匹配相同类型的异常对象, 也会捕捉目标异常类型的子类对象因此我们也可用一个 catch 捕获所有异常:(但不推荐!!!)
4.
alt + enter, 能够快速修正代码
如果本方法中没有合适的处理异常的方式, 就会沿着调用栈向上传递
这个向上传递的顺序是:本方法-->main方法--->JVM
举个例子:这个是func方法没有合适的处理异常的方式,因此沿着调用栈向上传递到main,发现找到了合适的处理异常的方式
如果向上一直传递都没有合适的方法处理异常, 最终就会交给 JVM 处理, 程序就会异常终止(和我们最开始未使用 try catch 时是一样的)
举例子:
说到这里,我们来总结下异常处理的流程吧
1.程序先执行 try 中的代码
2.如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
3.如果找到匹配的异常类型, 就会执行 catch 中的代码
4.如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
5.无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).
6.如果上层调用者也没有处理的了异常, 就继续向上传递.
7.一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止.
上面我们讲的叫捕捉异常,是 Java 内置的类会抛出一些异常 但有时候我们在处理异常的时候, 通常希望知道这段代码中究竟会出现哪些可能的异常. --->因此就引入了一个抛出异常的概念
抛出异常:使用 throws 关键字, 把可能抛出的异常显式的标注在方法定义的位置. 从而提醒调用者要注意捕获这些异常.
举一个例子:
异常的体系结构
全图在这儿:
Exception 是我们程序猿所使用的异常类的父类
Error 类或 RuntimeException 类的所有异常称为 非受查异常, 所有的其他异常称为 受查
异常.
我们上面说的都是运行时异常(非受查异常) 下面我们来说下受查异常(编译时异常)
代码抛出受查异常, 那么必须显式进行处理,显式处理的方式有两种:
查看 Scanner 的构造方法可以发现, 存在 FileNotFoundException 这样的异常说明,FileNotFoundException 这样的异常就是受查异常. 如果不显式处理, 编译无法通过.
1. 2.
自定义异常类
Java 中虽然已经内置了丰富的异常类, 但是我们实际场景中可能还有一些情况需要我们对异常类进行扩展, 创建符合我们实际情况的异常.
自定义异常通常会继承自 Exception 或者 RuntimeException
继承自 Exception 的异常默认是受查异常
继承自 RuntimeException 的异常默认是非受查异常.
举个例子:实现一个用户登陆功能.(这里我们继承自RuntimeException)
此时我们在处理用户名密码错误的时候可能就需要抛出两种异常. 我们可以基于已有的异常类进行扩展(继承), 创建和我们业务相关的异常类



