java7 异常继承_Java异常机制

异常概述

异常指程序运行中出现的不期而至的各种状况,如:文件找不到、网络连接失败、非法参数等。 异常发生在程序运行期间,它影响了正常的程序执行流程。

异常发生的原因有很多,通常包含以下几大类:

用户输入了非法数据。

要打开的文件不存在。

网络通信时连接中断,或者JVM内存溢出。

要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:

检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。

例如 要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。

运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。

错误:错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。

例如,当栈溢出 时,一个错误就发生了,它们在编译也检查不到的。

Java异常是一个描述在代码段中发生异常的对象,当发生异常情况时,一个代表该异常的对象被创 建并且在导致该异常的方法中被抛出,而该方法可以选择自己处理异常或者传递该异常。

异常体系结构

Java异常层次结构图

7bba3f7352c8c8b15be1f90568857dce.png

ef630d598f37483c68eb8012d26aefe0.png

Throwable是所有异常类型的子类

它分成了两个不同的分支

Error

Exception

其中又包含运行时异常RuntimeException和非运行时异常。

Java异常又可以其中异常类分为 不受检查异常 Unchecked Exception 和检查异常 Checked Exception。

Error

Error类对象由 Java 虚拟机生成并抛出

无法处理的错误,只能实现避免

大多数错误与代码编写者所执行的操作无关。

JVM运行错误 Virtual MachineError

JVM内存溢出 OutOfMemoryError

JVM执行应用时的 定义错误 NoClassDefFoundError 和 链接错误 LinkageError等

Exception

分类:

​编译时期异常:checked异常。在编译时期,就会检查,如果没有处理异常,则编译失败。(如日期格式化异常)

​运行时期异常:runtime异常。在运行时期,检查异常.在编译时期,运行异常不会编译器检测(不报错)。(如数学异常)

包含:

数组下标越界 ArrayIndexOutOfBoundsException

空指针异常 NullPointerException

算数异常 ArithmeticException

丢失资源 MissingResourceException

找不到类 ClassNotFoundException

这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生

Error 和 Exception的区别:

​Error通常是灾难性的致命的错误,是程序无法控制和处理的当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程;

​Exception通常是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。

检查异常和不受检查差异常

检查异常:在程序运行过程中,很容易出现的、情理可容的异常状况

在一定程度上这种异常的 发生是可以预测的,并且一旦发生该种异常,就必须采取某种方式进行处理。

除了RuntimeException及其子类以外,其他的Exception类及其子类都属于检查异常。

当程序中可能出现这类异常,要么用try-catch捕获,

要么用 throws 抛出,否则编译无法通 过。

不受检查异常:包括RuntimeException及其子类 和 Error。

不受检查异常为编译器不要求强制处理的异常

检查异常则是编译器要求必须处置的异常。

Java异常处理机制

java异常处理本质:抛出异常和捕获异常

对于所有的检查异常,Java规定:一个方法必须捕捉,或者声明抛出方法之外。

抛出异常

从当前环境中跳出,并把问题提交给上一级环境

抛出异常后, 会有几件事随之发生。

首先,是像创建普通的java对象一样将使用new在堆上创建一个异常对象;

然后,当前的执行路径(已经无法继续下去了)被终止,并且从当前环境中弹出对异常对象的引用。

最后,异常处理机制接管程序,并开始寻找异常处理程序 或者 异常处理器继续执行程序

捕获异常

当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适的异常处理器。

运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到合适异常处理器 执行异常的处理方法。

当运行时系统遍历调用栈而未找到合适的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。

处理异常

​throw -- 用于方法内抛出异常。

​throws -- 用在方法声明中,用于声明该方法可能抛出的异常。

​try -- 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常 时,异常就被抛出。

​catch -- 用于捕获异常。catch用来捕获try语句块中发生的异常。

​finally -- 其代码块总是会被执行。

​ 只有finally块执行完成之后,才会回来执行try或者catch块中的return或者throw语句

​ 如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。

抛出异常throw

使用抛出异常的方式来告诉 调用方法者 。

只是一个提示而已。

使用格式:

throw new 异常类名(参数);

注意:

1.throw关键字必须写在方法的内部

2.throw关键字后边new的对象必须是Exception或者Exception的子类对象

3.throw关键字抛出指定的异常对象,我们就必须处理这个异常对象

第一种处理:(终止程序)throw关键字后边创建的是RuntimeException或者是 RuntimeException的子类对象,我们可以不处理,默认交给JVM处理(打印异常对象,中断程序)

第二种处理:throw关键字后边创建的是编译异常(写代码的时候报错),我们就必须处理这个异常,要么throws(给别人处理),要么try...catch(自己处理)

例如:

throw new NullPointerException("要访问的arr数组不存在");

throw new ArrayIndexOutOfBoundsException("该索引在数组中不存在,已超出范围");

throw的使用:

public class Demo03Throw {

public static void main(String[] args) {

//int[] arr = null;

int[] arr = new int[3];

int e = getElement(arr,3);

System.out.println(e);

}

/*

定义一个方法,获取数组指定索引处的元素

参数:

int[] arr

int index

以后(工作中)我们首先必须对方法传递过来的参数进行合法性校验

如果参数不合法,那么我们就必须使用抛出异常的方式,告知方法的调用者,传递的参数有问题

注意:

NullPointerException是一个运行期异常,我们不用处理,默认交给JVM处理

ArrayIndexOutOfBoundsException是一个运行期异常,我们不用处理,默认交给JVM处理

*/

public static int getElement(int[] arr,int index){

/*

我们可以对传递过来的参数数组,进行合法性校验

如果数组arr的值是null

那么我们就抛出空指针异常,告知方法的调用者"传递的数组的值是null"

*/

if(arr == null){

throw new NullPointerException("传递的数组的值是null");

}

/*

我们可以对传递过来的参数index进行合法性校验

如果index的范围不在数组的索引范围内

那么我们就抛出数组索引越界异常,告知方法的调用者"传递的索引超出了数组的使用范围"

*/

if(index<0 || index>arr.length-1){

throw new ArrayIndexOutOfBoundsException("传递的索引超出了数组的使用范围");

}

int ele = arr[index];

return ele;

}

}

声明异常throws

将异常交给 调用者所处的方法 解决。

如果方法内通过throw抛出了编译时异常,而没有捕获处理(稍后讲解该方式),那么必须通过throws进行声明,让调用者去处理。

关键字throws运用于方法声明,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常).

注意:

1.throws关键字必须写在方法声明处

2.throws关键字后边声明的异常必须是Exception或者是Exception的子类

3.方法内部如果抛出了多个异常对象,那么throws后边必须也声明多个异常

如果抛出的多个异常对象有子父类关系,那么直接声明父类异常即可

4.调用了一个声明抛出异常的方法,我们就必须的处理声明的异常

要么继续使用throws声明抛出,交给方法的调用者处理,最终交给JVM

要么try...catch自己处理异常

声明异常格式:

修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{ }

声明异常的代码演示:

public class ThrowsDemo1 {

public static void main(String[] args) throws FileNotFoundException {

read("a.txt");

}

// 如果定义功能时有问题发生需要报告给调用者。可以通过在方法上使用throws关键字进行声明

public static void read(String path) throws FileNotFoundException {

if (!path.equals("a.txt")) {//如果不是 a.txt这个文件

// 我假设 如果不是 a.txt 认为 该文件不存在 是一个错误 也就是异常 throw

throw new FileNotFoundException("文件不存在");

}

}

}

throws用于进行异常类的声明,若该方法可能有多种异常情况产生,那么在throws后面可以写多个异常类,用逗号隔开。

public class ThrowsDemo2 {

public static void main(String[] args) throws IOException {

read("a.txt");

}

public static void read(String path)throws FileNotFoundException, IOException {

if (!path.equals("a.txt")) {//如果不是 a.txt这个文件

// 我假设 如果不是 a.txt 认为 该文件不存在 是一个错误 也就是异常 throw

throw new FileNotFoundException("文件不存在");

}

if (!path.equals("b.txt")) {

throw new IOException();

}

}

}

捕获异常try…catch

匹配相应的异常 并对其进行处理。

语法:

try{

编写可能会出现异常的代码

}catch(异常类型 e){

处理异常的代码

//记录日志/打印异常信息/继续抛出异常

}

try:该代码块中编写可能产生异常的代码。

catch:对捕获到的异常进行处理。

例如:

public class TryCatchDemo {

public static void main(String[] args) {

try {// 当产生异常时,必须有处理方式。要么捕获,要么声明。

read("b.txt");

} catch (FileNotFoundException e) {// 括号中需要定义什么呢?

//try中抛出的是什么异常,在括号中就定义什么异常类型

System.out.println(e);

}

System.out.println("over");

}

/*

*

* 我们 当前的这个方法中 有异常 有编译期异常

*/

public static void read(String path) throws FileNotFoundException {

if (!path.equals("a.txt")) {//如果不是 a.txt这个文件

// 我假设 如果不是 a.txt 认为 该文件不存在 是一个错误 也就是异常 throw

throw new FileNotFoundException("文件不存在");

}

}

}

如何获取异常信息:

Throwable类中定义了一些查看方法:

public String getMessage():获取异常的描述信息,原因(提示给用户的时候,就提示错误原因。

public String toString():获取异常的类型和异常描述信息(不用)。

public void printStackTrace():打印异常的跟踪栈信息并输出到控制台。

​ 包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。

finally 代码块

finally:有一些特定的代码无论异常是否发生,都需要执行。另外,因为异常会引发程序跳转,导致有些语句执行不到。而finally就是解决这个问题的,在finally代码块中存放的代码都是一定会被执行的。

什么时候的代码必须最终执行?

当我们在try语句块中打开了一些物理资源(磁盘文件/网络连接/数据库连接等),我们都得在使用完之后,最终关闭打开的资源。

finally的语法:

try...catch....finally:自身需要处理异常,最终还得关闭资源。

注意:finally不能单独使用。

比如在我们之后学习的IO流中,当打开了一个关联文件的资源,最后程序不管结果如何,都需要把这个资源关闭掉。

finally代码参考如下:

public class TryCatchDemo4 {

public static void main(String[] args) {

try {

read("a.txt");

} catch (FileNotFoundException e) {

//抓取到的是编译期异常 抛出去的是运行期

throw new RuntimeException(e);

} finally {

System.out.println("不管程序怎样,这里都将会被执行。");

}

System.out.println("over");

}

/*

*

* 我们 当前的这个方法中 有异常 有编译期异常

*/

public static void read(String path) throws FileNotFoundException {

if (!path.equals("a.txt")) {//如果不是 a.txt这个文件

// 我假设 如果不是 a.txt 认为 该文件不存在 是一个错误 也就是异常 throw

throw new FileNotFoundException("文件不存在");

}

}

}

当只有在try或者catch中调用退出JVM的相关方法,此时finally才不会执行,否则finally永远会执行。

异常注意事项

多个异常使用捕获又该如何处理呢?

多个异常分别处理。

多个异常一次捕获,多次处理。

多个异常一次捕获一次处理。

一般我们是使用一次捕获多次处理方式,格式如下:

try{

编写可能会出现异常的代码

}catch(异常类型A e){ 当try中出现A类型异常,就用该catch来捕获.

处理异常的代码

//记录日志/打印异常信息/继续抛出异常

}catch(异常类型B e){ 当try中出现B类型异常,就用该catch来捕获.

处理异常的代码

//记录日志/打印异常信息/继续抛出异常

}

注意:这种异常处理方式,catch中异常类型的顺序应该是

先小后大,即先子类后父类

运行时异常被抛出可以不处理。即不捕获也不声明抛出。

如果finally有return语句,永远返回finally中的结果,避免该情况.

如果父类抛出了多个异常, 子类重写父类方法时

抛出 父类相同的异常 或 父类异常的子类 或 不抛出异常。

父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。

此时子类产生该异常,只能捕获处理,不能声明抛出

自定义异常

概述

在开发中总是有些异常情况是java里没有定义好的。此时我们根据自己业务的异常情况来定义异常类。

例如年龄负数问题,考试成绩负数问题等等。

自定义异常类也就是 在开发中 根据自己业务的异常情况 定义的异常类。

如何定义

格式:

public class XXXExcepiton extends Exception | RuntimeException{

//添加一个空参数的构造方法

//添加一个带异常信息的构造方法

}

注意:

1.自定义异常类一般都是以Exception结尾,说明该类是一个异常类

2.自定义异常类,必须的继承Exception或者RuntimeException

继承Exception:那么自定义的异常类就是一个编译期异常,如果方法内部抛出了编译期异常,就必须处理这个异常,要么throws,要么try...catch

继承RuntimeException:那么自定义的异常类就是一个运行期异常,无需处理,交给虚拟机处理(中断处理)

public class RegisterException extends /*Exception*/ RuntimeException{

//添加一个空参数的构造方法

public RegisterException(){

super();

}

/*

添加一个带异常信息的构造方法

查看源码发现,所有的异常类都会有一个带异常信息的构造方法,

方法内部会调用父类带异常信息的构造方法,

让父类来处理这个异常信息

*/

public RegisterException(String message){

super(message);

}

}

练习

要求:我们模拟注册操作,如果用户名已存在,则抛出异常并提示:亲,该用户名已经被注册。

首先定义一个登陆异常类RegisterException:

// 业务逻辑异常

public class RegisterException extends Exception {

/**

* 空参构造

*/

public RegisterException() {

}

/**

*

* @param message 表示异常提示

*/

public RegisterException(String message) {

super(message);

}

}

模拟登陆操作,使用数组模拟数据库中存储的数据,并提供当前注册账号是否存在方法用于判断。

public class Demo {

// 模拟数据库中已存在账号

private static String[] names = {"bill","hill","jill"};

public static void main(String[] args) {

//调用方法

try{

// 可能出现异常的代码

checkUsername("nill");

System.out.println("注册成功");//如果没有异常就是注册成功

}catch(RegisterException e){

//处理异常

e.printStackTrace();

}

}

//判断当前注册账号是否存在

//因为是编译期异常,又想调用者去处理 所以声明该异常

public static boolean checkUsername(String uname) throws LoginException{

for (String name : names) {

if(name.equals(uname)){//如果名字在这里面 就抛出登陆异常

throw new RegisterException("亲"+name+"已经被注册了!");

}

}

return true;

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值