一、异常机制
异常框架结构
基本概念
- 异常就是"不正常"的含义,在Java语言中主要指程序执行中发生的不正常情况
- java.lang.Throwable类是Java语言中所有错误(Error)和异常(Exception)的超类
- 其中Error类主要用于描述Java虚拟机无法解决的严重错误,通常无法编码解决,如:JVM挂掉了
- 其中Exception类主要用于描述因编程错误或偶然外在因素导致的轻微错误,通常可以编码解决,如:0作为除数等
异常的分类
- java.lang.Exception类是所有异常的超类,主要分为以下两种:
- RuntimeException - 运行时异常,也叫作非检测性异常,编译时无异常,运行时报错
- IOException和其它异常 - 其它异常,也叫作检测性异常,所谓检测性异常就是指在编译阶段都能被编译器检测出来的异常
- 其中RuntimeException类的主要子类:
- ArithmeticException类 - 算术异常
- ArrayIndexOutOfBoundsException类 - 数组下标越界异常
- NullPointerException - 空指针异常
- ClassCastException - 类型转换异常
- NumberFormatException - 数字格式异常
- 注意:
当程序执行过程中发生异常但又没有手动处理时,则由Java虚拟机采用默认方式处理异常,而默认处理方式就是:打印异常的名称、异常发生的原因、异常发生的位置以及终止程序
异常的避免
- 在以后的开发中尽量使用 if 条件判断来避免异常的发生
- (缺点)过多的 if 条件判断会导致程序的代码加长、臃肿,可读性差
异常的捕获
- 语法格式
try {
编写可能发生异常的代码;
}
catch(异常类型 引用变量名) {
编写针对该类异常的处理代码;
}
catch(异常类型2 引用变量名2){
编写针对该类异常的处理代码;
}
...
finally {
编写无论是否发生异常都要执行的代码;
}
- 注意事项
-
当需要编写多个catch分支时,切记小类型应该放在大类型的前面;
-
懒人的写法:
catch(Exception e) {} // 懒人写法,也能正常捕获异常,但是代码可读性不高,开发中不推荐
-
finally通常用于进行善后处理,如:关闭已经打开的文件等
- 用法举例
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class ExceptionCatchTest {
public static void main(String[] args) {
// 创建一个FileInputStream类型的对象与d:/a.txt文件关联,打开文件
FileInputStream fis = null;
try {
System.out.println("1");
// 编写可能发生异常的代码
fis = new FileInputStream("d:/a.txt"); // 当程序在这发生异常时,直奔catch内代码
System.out.println("2");
} catch (FileNotFoundException e) {
System.out.println("3");
e.printStackTrace(); // 编写针对该类异常的处理代码
System.out.println("4");
}
// 关闭文件
try {
System.out.println("5");
fis.close();
System.out.println("6");
} /*catch (Exception e) { // 放在这里的话,下面的catch语句就会报错
e.printStackTrace();
}*/ catch (IOException e) {
System.out.println("7");
e.printStackTrace();
System.out.println("8");
} catch (NullPointerException e) {
e.printStackTrace();
} catch (Exception e) {// 懒人写法(运行结果是一样的),不推荐,推荐上面写法,提高可读性
e.printStackTrace();
}
System.out.println("世界上最真情的相依就是你在try我在catch,无论你发神马脾气我都默默承受 并静静的处理,到那时再来期待我们的finally!");
// 当程序执行过程中没有发生异常时的执行流程:1 2 5 6 世界上...
// 当程序执行过程中发生异常又没有手动处理空指针异常时的执行流程:1 3 4 5空指针异常导致程序终止
// 当程序执行过程中发生异常并且手动处理空指针异常时的执行流程: 1 3 4 5 世界上...
// 手动处理异常和没有处理的区别:代码是否可以继续向下执行
}
}
public class ExceptionFinallyTest {
// 笔试考点
public static int test() {
try {
int[] arr = new int[5];
System.out.println(arr[5]);
return 0;
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
return 1; // 最后返回的不是1
} finally {
return 2; // 返回了2,提交结束方法并返回数据
}
}
public static void main(String[] args) {
try {
int ia = 10;
int ib = 0;
System.out.println(ia / ib);
} catch (ArithmeticException e) {
e.printStackTrace();
String str1 = null;
//str1.length(); // 会发生空指针异常
} finally {
System.out.println("无论是否发生异常都记得来执行我哦!"); // 依然是执行
}
System.out.println("Over!"); // catch中又有新异常就不执行了
System.out.println("----------------------------------------");
int test = test();
System.out.println("test = " + test); // 2
}
}
异常的抛出
-
基本概念
在某些特殊情况下有些异常不能处理或者不便于处理时,就可以将该异常转移给该方法的调用者,这种方法就叫异常的抛出。当方法执行时出现异常,则底层生成一个异常类对象抛出,此时异常代码后续的代码就不再执行 -
语法格式
访问权限 返回值类型 方法名称(形参列表) throws 异常类型1,异常类型2,...{ 方法体; } 如: public void show() throws IOException{}
-
方法重写的原则
- 要求方法名相同、参数列表相同以及返回值类型相同,从jdk1.5开始支持返回子类类型
- 要求方法的访问权限不能变小,可以相同或者变大
- 要求方法不能抛出更大的异常
import java.io.IOException;
public class ExceptionMethod {
public void show() throws IOException {}
}
public class SubExceptionMethod extends ExceptionMethod {
@Override
public void show() throws IOException {} // 子类重写的方法可以抛出和父类中方法一样的异常
//public void show() throws FileNotFoundException {} // 子类重写的方法可以抛出更小的异常
//public void show() {} // 子类可以不抛出异常
//public void show() throws ClassNotLoadedException {} // 不可以抛出平级但不一样的异常
//public void show() throws Exception {} // 不可以抛出更大的异常
}
- 注意:
子类重写的方法不能抛出更大的异常、不能抛出平级不一样的异常,但可以抛出一样的异常、更小的异常以及不抛出异常
main方法(程序入口)不建议抛出异常(会加重jvm的负担),建议在main方法中自行捕获处理
- 经验分享
- 若父类中被重写的方法没有抛出异常时,则子类中重写的方法只能进行异常的捕获处理
- 若一个方法内部又以递进方式分别调用了好几个其它方法,则建议这些方法内可以使用抛出的方法处理到最后一层进行捕获方式处理
- 用法举例
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class ExceptionThrowsTest {
public static void show() throws IOException { // 将异常抛给调用者test1
FileInputStream fis = new FileInputStream("d:/a.txt");//异常代码后的代码就不再执行
System.out.println("我想看看你抛出异常后是否继续向下执行???"); // 不执行
fis.close();
}
public static void test1() throws IOException { // 将异常抛给调用者test2
show();
}
public static void test2() throws IOException { // 将异常抛给调用者test3
test1();
}
public static void test3() throws IOException { // 将异常抛给调用者main方法体
test2();
}
// 不建议在main方法中抛出异常 JVM负担很重
public static void main(String[] args) /*throws IOException*/ {
// 所以不抛出,而是就地处理
try {
show();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("------------------------------------");
// main方法体中处理异常
try {
test3();
} catch (IOException e) {
e.printStackTrace();
}
}
}
自定义异常
-
基本概念
当需要在程序中表达年龄不合理的情况时,而Java官方又没有提供这种针对性的异常,此时就需要程序员自定义异常加以描述 -
实现流程
- 自定义xxxException异常类 继承 Exception类或者其子类
- 提供两个版本的构造方法,一个是无参构造方法,另外一个是字符串作为参数的构造方法
-
异常的产生
throw new 异常类型(实参); 如: throw new AgeException("年龄不合理!!!");
-
Java采用的异常处理机制是将异常处理的程序代码集中在一起,与正常的程序代码分开,使得程序简洁、优雅,并易于维护
-
用法举例
public class AgeException extends Exception {
// 序列化的版本号与序列化操作有关系
static final long serialVersionUID = 7818375828146090155L;
public AgeException() {
}
public AgeException(String message) {
super(message);
}
}
注:本内容为个人拉勾教育大数据训练营学习课程笔记