1.异常的概述
什么是异常
是一个在程序执行期间发生的事件,它中断正在执行程序的正常指令流。为了能够及时有效地处理程序中的运行错误,必须使用异常类
异常类
- 在Java中将异常的信息封装成一个类
- 当程序出现异常时,就会创建异常类对象并抛出异常相关的信息(如异常出现的位置,原因)
常见的异常
- 数组越界异常:ArrayIndexOutOfBoundsException
- 空指针异常:NullPointerException
- 算数异常以0作除数:ArithmeticException: / by zero
2.异常的继承体系和错误的区别
异常的继承体系
在 Java 中所有异常类型都是内置类 java.lang.Throwable 类的子类,即 Throwable 位于异常类层次结构的顶层
Throwable类下有两个分支 Exception 和 Error 如下图所示
由图所知,Throwable 类是所有异常和错误的超类,下面有 Error 和 Exception 两个子类分别表示错误和异常。其中异常类 Exception 又分为运行时异常和非运行时异常,这两种异常有很大的区别,也称为不检查异常(Unchecked Exception)和检查异常(Checked Exception)
- 运行时异常:都是 RuntimeException 类及其子类异常,如 NullPointerException、IndexOutOfBoundsException 等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般由程序逻辑错误引起,程序应该从逻辑角度尽可能避免这类异常的发生。
- 非运行时异常:是指 RuntimeException 以外的异常,类型上都属于 Exception 类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如 IOException、ClassNotFoundException 等以及用户自定义的 Exception 异常,
异常与错误的区别
异常
- 指程序在编译期间,运行时期发生了某种异常,我们可以对异常进行处理
- 如若不处理异常,程序将会结束
- 举了例子
public static void main(String[] args) {
int[] arr = {1,2,3};
System.out.println(arr[3]);
//该句运行时会发生数组越界异常ArraylndexOutOfBoundException
//由于没有处理异常,导致程序无法继续进行,无法打印程序结束
System.out.println("程序结束");
}
错误
描述Java运行时内部出错或者资源耗尽错误(只能终止程序)
Error
错误的发生往往都是系统级别的问题,都是 JVM 所在系统发生的,并反馈给 JVM无法针对处理,只能修改代码
举了例子
public static void main(String[] args) {
int array[] = new int[1024*1024*1024];
//该句运行时JVM无法分配足够的空间,发生了内存溢出错误OutOfMemoryError
//程序也会立即结束,不会打印程序结束
System.out.println("程序结束");
}
3.异常对象的产生原因和处理方式
异常对象的产生原因
举个例子
class ArrayTools{
//对给定的数组通过给定的角标获取元素。
public static int getElement(int[] arr,int index) {
int element = arr[index];
return element;
}
}
public class Test {
public static void main(String[] args) {
int[] arr = {34,12,67};
int num = ArrayTools.getElement(arr,4);//静态方法用类名调用,不依赖于对象
System.out.println("num="+num);
System.out.println("程序结束");
}
}
原因分析
- 由于没有找到索引 4,导致运行时发生了异常。这个异常JVM认识:ArrayIndexOutOfBoundsException 这个异常Java本身有描述:异常的名称、异常的内容、异常的产生位置
- Java将这些信息直接封装到对象中,new ArrayIndexOutOfBoundsException(4) , JVM将产生的异常抛给调用者main() 方法
- main() 方法接收到了数组索引越界异常对象。由于main()方法并没有进行处理异常,main()方法就会继续把异常抛给调用者JVM
- 当JVM收到异常后,将异常对象中的名称、异常内容、位置都显示在就控制台上。同时让程序立刻终止
异常的处理方式
JVM的默认处理方法
- 把异常的名称,原因,位置等信息输出在控制台,同时立即结束程序
- 一旦有异常出现,代码将不再往下继续进行
解决程序中异常的手动方式
- 编写处理代码
try...catch...finally
- 抛出
throws
4.方法内部抛出对象throw关键字
在java中,提供了一个throw
关键字,它用来抛出一个指定的异常对象
什么时候使用throw关键字?
当调用方法使用接受到的参数时,首先需要先对参数数据进行合法的判断,数据若不合法,就应该告诉调用者,传递合法的数据进来。这时需要使用抛出异常的方式来告诉调用者。
使用
throw
关键字具体操作
- 创建一个异常对象,封装一些提示信息(信息可以自己编写)
- 通过关键字 throw 将这个异常对象告知给调用者。throw 异常对象
- throw 用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并且结束当前方法的执行
throw关键字使用格式
- throw new 异常类名(参数)
- eg:throw new NullPointerException("数组为null");
- eg:throw new ArrayIndexOutOfBoundsException("数组越界");
5.方法声明异常关键字throws
声明
将可能出现的问题标识出来,报告给调用者。如果方法内部通过 throw 抛出了编译时异常,而没有捕获处理(稍后讲解该部分),那么必须通过 throws 进行声明,让调用者去处理
声明异常格式
修饰符 放回值类型 方法名(参数) throws 异常类名1,异常类名2...{ }
注意事项:
若方法可能出现多种异常,那么可以在 throws 关键字后写多个异常类名,用逗号隔开
6.try...catch异常处理
捕获
Java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定性的处理
捕获异常的格式
try{ //需要被检测的语句(可能出现异常的语句) } catch (异常类 变量) { //异常的处理方式 } finally{ //一定会被执行的语句 }
格式说明
- try:该代码块中可能会产生异常的语句
- catch:用来对某种异常的捕获,实现对捕获到的异常进行处理
- finally:这部分的代码是一定会被执行的。异常的出现会导致代码不再执行,而一些特定的代码无论异常是否产生,都需要执行
- try...catch... 处理异常后,程序将可以继续进行
多catch处理
一个 try 多个 catch 组合
对代码进行异常检测,并对检测的异常传递给catch处理。对每种异常信息进行不同的捕获处理
多catch处理的格式
注意:在捕获异常处理时,变量也是有作用域的,如可以定义多个异常变量名 e
void show() { //不用throws
try{
throw new Exception();//产生异常直接捕获处理
}
catch (XxxException e) {
//处理方式
}
catch (YyyException e) {
//处理方式
}
catch (ZzzException e) {
//处理方式
}
finally{
//处理方式
}
多 catch 处理细节
- 多个catch小括号中,写的是异常类的类名,有没有顺序的影响。
- 平级异常:抛出的异常类之间,没有继承关系,没有顺序
- 上下级关系的异常:越高级的父类,越写在下面
7.finally代码块
finally 代码块的特点
被
finally
控制的语句体一定会执行finally的作用
finally
无论程序是否有异常出现,程序必须执行释放资源,如:IO流操作和数据库操作中会见到
8.运行时期异常的特点
运行时期异常的概述
RuntimeException和他的所有子类异常都属于运行时期异常
NullPointerException,ArrayIndexOutOfBoundsException等都属于运行时期异常
运行时期异常的特点
- 方法中抛出运行时期异常,方法定义中无需 throws 声明,调用者也无需处理此异常。
- 运行时期异常一旦发生,需要程序人员修改源代码。
9.自定义异常
自定义异常的定义
阅读源码发现:每个异常中都调用了父类的构造方法,把异常描述信息传递给了父类,让父类帮我们进行异常信息的封装
格式
Class 异常名 extends Exception{ //或继承RuntimeException
public 异常名(){
}
public 异常名(String s){
super(s);
}
}
自定义异常的练习
- 在 Person 类的有参构造方法中,进行年龄的判断,年龄为负数或者大于200岁的,则抛出 NoAgeException异常,异常提示信息“年龄数值非法”
- 在测试类中,调用有参的构造方法,完成Person类对象的创建,应对异常进行处理
关于构造方法抛出异常总结
构造函数到底抛出这个
NoAgeException
是继承Exception
呢?还是继承RuntimeException
呢?
- 继承
Exception
,必须要 throws 声明,一声明就告知调用者进行捕获,一旦问题处理了调用者的程序会继续执行- 继承
RuntimeExcpetion
,不需要 throws 声明的,这时调用是不需要编写捕获代码的,因为调用根本就不知道有问题- 一旦发生
NoAgeException
,调用者程序会停掉,并有JVM将信息显示到屏幕,让调用者看到问题,修正代码。