## 一、概述
### 1.1 什么是异常?
* 异常本质上是程序上的错误。
* 错误在我们编写程序的过程中会经常发生,包括编译期间和运行期间的错误。
Java 中的异常(Exception)是一个在程序执行期间发生的事件,它中断正在执行程序的正常指令流。为了能够及时有效地处理程序中的运行错误,必须使用异常类。
### 1.2 异常分类
在 Java 中所有异常类型都是内置类 java.lang.Throwable 类的子类。Throwable 类下有两个异常分支 Exception 和 Error。
* Exception 类用于用户程序可能出现的异常情况,它也是用来创建自定义异常类型类的类。
* Error 定义了在通常环境下不希望被程序捕获的异常。Error 类型的异常用于 Java 运行时由系统显示与运行时系统本身有关的错误。它们通常是灾难性的致命错误,不是程序可以控制的。
:-: ![](http://cndpic.dodoke.com/5a3bddc647e617d2f6d34818060a0749)
其中异常类 Exception 又分为运行时异常和非运行时异常,这两种异常有很大的区别,也称为不检查异常(Unchecked Exception)和检查异常(Checked Exception)。
* `运行时异常`都是 RuntimeException 类及其子类异常,如 NullPointerException、IndexOutOfBoundsException 等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般由程序逻辑错误引起,程序应该从逻辑角度尽可能避免这类异常的发生。
* `非运行时异常`是指 RuntimeException 以外的异常,类型上都属于 Exception 类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如 IOException、ClassNotFoundException 等以及用户自定义的 Exception 异常,一般情况下不自定义检查异常。
【选择】下列代码中的异常属于()(选择两项)
```
int a = 0;
System.out.println(2 / a);
```
```
A 非检查型异常 B 检查型异常 C Error D Exception
```
【选择】()类及其子类所表示的异常是用户程序无法处理的。(选择一项)
```
A NumberFormatException B Exception C Error D RuntimeException
```
### 1.3 异常处理机制
Java 的异常处理机制提供了一种结构性和控制性的方式来处理程序执行期间发生的事件。异常处理通过 5 个关键字来实现:try、catch、throw、throws 和 finally。异常处理的机制如下:
* 在方法中用 try catch 语句捕获并处理异常,catch 语句可以有多个,用来匹配多个异常。
* 对于处理不了的异常或者要转型的异常,在方法的声明处通过 throws 语句拋出异常,即由上层的调用方法来处理。
```
try {
逻辑程序块
} catch (异常类型1 e) {
处理代码块1
} catch (异常类型2 e) {
处理代码块2
throw(e); // 再抛出这个“异常”
} finally {
释放资源代码块
}
```
## 二、使用 try-catch-finally 实现异常处理
在 Java 中通常采用 try-catch-finally 语句来捕获异常并处理。语法格式如下:
```
try {
逻辑代码块1
} catch (异常类型 e) {
处理代码块1
} finally {
释放资源代码
}
```
* 在以上语法中,把可能引发异常的语句封装在 try 语句块中,用以捕获可能发生的异常。
* 如果 try 语句块中发生异常,那么一个相应的异常对象就会被拋出,然后 catch 语句就会依据所拋出异常对象的类型进行捕获,并处理。处理之后,程序会跳过 try 语句块中剩余的语句,转到 catch 语句块后面的第一条语句开始执行。
* 如果 try 语句块中没有异常发生,那么 try 块正常结束,后面的 catch 语句块被跳过,程序将从 catch 语句块后的第一条语句开始执行。
在以上语法的处理代码块1中,可以使用以下 3 个方法输出相应的异常信息。
* `printStackTrace()`方法:指出异常的类型、性质、栈层次及出现在程序中的位置。
* `getMessage()`方法:输出错误的性质。
* `toString()`方法:给出异常的类型与性质。
【例题】编写一个录入学生姓名、年龄和性别的程序,要求能捕捉年龄不为数字时的异常。
```
public class Student {
private String name;
private int age;
public Student() {}
public Student(String name, int age) {
this.setName(name);
this.setAge(age);
}
// getter setter...
// toString()
}
public class Test {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String name = "";
int age = 0;
try {
System.out.println("请输入学生姓名:");
name = sc.next();
System.out.println("请输入学生年龄:");
age = sc.nextInt();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(new Student(name, age));
}
}
```
在实际开发中,根据 try catch 语句的执行过程,try 语句块和 catch 语句块有可能不被完全执行,而有些处理代码则要求必须执行。例如,程序在 try 块里打开了一些物理资源(如数据库连接、网络连接和磁盘文件等),这些物理资源都必须显式回收。
> Java 的垃圾回收机制不会回收任何物理资源,垃圾回收机制只回收堆内存中对象所占用的内存。
使用 try-catch-finally 语句时需注意以下几点:
1. 异常处理语法结构中只有 try 块是必需的,也就是说,如果没有 try 块,则不能有后面的 catch 块和 finally 块;
2. catch 块和 finally 块都是可选的,但 catch 块和 finally 块至少出现其中之一,也可以同时出现;
3. 可以有多个 catch 块,捕获父类异常的 catch 块必须位于捕获子类异常的后面;
4. 不能只有 try 块,既没有 catch 块,也没有 finally 块;
5. 多个 catch 块必须位于 try 块之后,finally 块必须位于所有的 catch 块之后。
6. finally 与 try 语句块匹配的语法格式,此种情况会导致异常丢失,所以不常见。
:-: ![](http://cndpic.dodoke.com/55e91aa6d9f853c9a34c62726567324a)
但是当 try、catch、finally 中加入 return 之后,return 和 finally 的执行顺序让很多人混淆不清。
* `try`中带有`return`
```
public static int show() {
try {
return 1;
} finally {
System.out.println("执行finally模块");
}
}
// 执行 fianlly 模块
// 1
```
* `try`和`catch`中都带有`return`
```
public static int show() {
try {
int a = 8 / 0;
return 1;
} catch (Exception e) {
return 2;
} finally {
System.out.println("执行finally模块");
}
}
// 执行 finally 模块
// 2
```
> 当 try 代码块或者 catch 代码块中有 return 时,finally 中的代码总会被执行,且 finally 语句 return 返回之前执行。
* `finally`中带有`return`
```
public static int show() {
try {
int a = 8 / 0;
return 1;
} catch (Exception e) {
return 2;
} finally {
System.out.println("执行finally模块");
return 0;
}
}
// 执行 finally 模块
// 0
```
> 当 finally 有返回值时,会直接返回该值,不会去返回 try 代码块或者 catch 代码块中的返回值。
* `finally`中改变返回值
```
public static int show() {
int result = 0;
try {
return result;
} finally {
System.out.println("执行finally模块");
result = 1;
}
}
// 执行finally模块
// 0
```
> 由输出结果可以看出,在 finally 代码块中改变返回值并不会改变最后返回的内容。
总结为以下几条:
* 当 try 代码块和 catch 代码块中有 return 语句时,finally 仍然会被执行。
* 执行 try 代码块或 catch 代码块中的 return 语句之前,都会先执行 finally 语句。
* 无论在 finally 代码块中是否修改返回值,返回值都不会改变,仍然是执行 finally 代码块之前的值。
* finally 代码块中的 return 语句一定会执行。
【选择】数组下标越界,则发生异常,提示为()(选择一项)
```
A IOException
B ArithmeticException
C SQLException
D ArrayIndexOutOfBoundsException
```
【阅读】运行下列代码,当输入的 num 值为 a 时,系统会输出()
```
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
try {
int num = input.nextInt();
System.out.println("one");
} catch(Exception e) {
System.out.println("two");
} finally {
System.out.println("three");
}
System.out.println("end");
}
```
【阅读】运行下列代码,输出结果为()
```
public static void main (String[] args) {
try {
int a = 1 - 1;
System.out.println("a = " + a);
int b = 4 / a;
int c[] = {1};
c[10] = 99;
} catch (ArithmeticException e) {
System.out.println("除数不允许为零");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界");
}
}
```
【选择】下列关于异常的描述,错误的是()(选择两项)
```
A printStackTrace() 用来跟踪异常事件发生时执行堆栈的内容
B catch 块中可以出现同类型异常
C 一个 try 块可以包含多个 catch 块
D 捕获到异常后将输出所有 catch 语句块的内容
```
【阅读】假设要输出的 id 值为 a101,name 值为 Tom,程序的执行结果为()
```
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
try {
int id = input.nextInt();
String name = input.next();
System.out.println("id=" + id);
System.out.println("name=" + name);
} catch (InputMismatchException e) {
System.out.println("输入有误");
System.exit(1);
e.printStackTrace();
} finally {
System.out.println("输入结束");
}
}
```
【阅读】下列代码的运行结果为()
```
public static int test(int b) {
try {
b += 10;
return b;
} catch (Exception e) {
return 1;
} finally {
b += 20;
return b;
}
}
public static void main (String[] args) {
int num = 10;
System.out.println(test(num));
}
```
## 三、使用 throw 和 throws 实现异常处理
Java 中的异常处理除了包括捕获异常和处理异常之外,还包括声明异常和拋出异常,可以通过 throws 关键字在方法上声明该方法要拋出的异常,然后在方法内部通过 throw 拋出异常对象。
* throws 用来声明一个方法可能抛出的所有异常信息,throw 则是指拋出的一个具体的异常类型。
* 通常在一个方法(类)的声明处通过 throws 声明方法(类)可能拋出的异常信息,而在方法(类)内部通过 throw 声明一个具体的异常信息。
* throws 通常不用显示地捕获异常,可由系统自动将所有捕获的异常信息抛给上级方法; throw 则需要用户自己捕获相关的异常,而后再对其进行相关包装,最后将包装后的异常信息抛出。
### 3.1 throws 声明异常
当一个方法产生一个它不处理的异常时,那么就需要在该方法的头部声明这个异常,以便将该异常传递到方法的外部进行处理。可以使用 throws 关键字在方法的头部声明一个异常,其具体格式如下:
```
返回值类型 方法名(参数列表) throws Exception 1, Exception2, … { … }
```
```
public static int test(int a, int b) throws ArithmeticException {
if (b == 0) {
throw new ArithmeticException("算术异常");
} else {
return a / b;
}
}
```
> 注意:在编写类继承代码时要注意,子类在覆盖父类带 throws 子句的方法时,子类的方法声明中的 throws 子句不能出现父类对应方法的 throws 子句中没有的异常类型,因此 throws 子句可以限制子类的行为。也就是说,子类方法拋出的异常不会超过父类定义的范围。
### 3.2 throw 抛出异常
throw 语句用来直接拋出一个异常,后接一个可拋出的异常类对象,其语法格式如下:
```
throw ExceptionObject;
```
> ExceptionObject 必须是 Throwable 类或其子类的对象。如果是自定义异常类,也必须是 Throwable 的直接或间接子类。
【选择】下列关于这段代码的说法正确的是()(选择两项)
```
public static void test (String str) throws Exception {
if (str == null || str.length == 0) {
throw new Exception("参数不能为空");
} else {
System.out.println("str = " + str);
}
}
public static void main(String[] args) throws Exception {
Scanner input = new Scanner(System.in);
String str = input.nextLine();
test(str);
}
```
```
A 代码错误,没有对 test() 方法中抛出的异常进行处理
B 若产生异常,将由系统进行异常处理
C 本段代码对 throw 抛出异常对象的处理方法为自己抛出异常自己处理
D 若输入空字符串,代码运行结果为:java.lang.Exception: 参数不能为空
```
【选择】在下列代码划线处不可以填入选项中的哪一个异常类型()(选择一项)
```
public static int test(int a, int b) throws ____ {
if (b == 0) {
throw new ArithmeticException("算术异常");
} else {
return a / b;
}
}
```
```
A Throwable
B Exception
C InputMismatchException
D ArithmeticException
```
## 四、自定义异常
如果[](http://c.biancheng.net/java/) Java 提供的内置异常类型不能满足程序设计的需求,这时我们可以自己设计 Java 类库或框架,其中包括异常类型。实现自定义异常类需要继承 Exception 类或其子类,如果自定义运行时异常类需继承 RuntimeException 类或其子类。
自定义异常的语法形式为:
~~~
class 自定义异常名 extends Exception
~~~
在编码规范上,一般将自定义异常类的类名命名为 XXXException,其中 XXX 用来代表该异常的作用。
自定义异常类一般包含两个构造方法:一个是无参的默认构造方法,另一个构造方法以字符串的形式接收一个定制的异常消息,并将该消息传递给超类的构造方法。
【例题】编写一个程序,对会员注册时的年龄进行验证,即检测是否在 0~100 岁。
```
public class MyException extends Exception {
public MyException() {
super();
}
public MyException(String str) {
super(str);
}
}
import java.util.InputMismatchException;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
int age;
Scanner input = new Scanner(System.in);
System.out.println("请输入您的年龄:");
try {
age = input.nextInt(); // 获取年龄
if(age < 0) {
throw new MyException("您输入的年龄为负数!输入有误!");
} else if(age > 100) {
throw new MyException("您输入的年龄大于100!输入有误!");
} else {
System.out.println("您的年龄为:"+age);
}
} catch(InputMismatchException e1) {
System.out.println("输入的年龄不是数字!");
} catch(MyException e2) {
System.out.println(e2.getMessage());
}
}
}
```
- 阶段一 Java 零基础入门
- 步骤1:基础语法
- 第01课 初识
- 第02课 常量与变量
- 第03课 运算符
- 第04课 选择结构
- 第05课 循环结构
- 第06课 一维数组
- 第08课 方法
- 第09课 数组移位与统计
- 第10课 基础语法测试
- 第09课 基础语法测试(含答案)
- 步骤2:面向对象
- 第01课 类和对象
- 第02课 封装
- 第03课 学生信息管理
- 第04课 继承
- 第05课 单例模式
- 第06课 多态
- 第07课 抽象类
- 第08课 接口
- 第09课 内部类
- 第10课 面向对象测试
- 第10课 面向对象测试(含答案)
- 步骤3:常用工具类
- 第01课 异常
- 第02课 包装类
- 第03课 字符串
- 第04课 集合
- 第05课 集合排序
- 第06课 泛型
- 第07课 多线程
- 第08课 输入输出流
- 第09课 案例:播放器
- 第10课 常用工具测试(一)
- 第10课 常用工具测试(一)(答案)
- 第10课 常用工具测试(二)
- 第10课 常用工具测试(二)(答案)
- 阶段二 从网页搭建入门 JavaWeb
- 步骤1:HTML 与 CSS
- 第01课 HTML 入门
- 第01课 HTML 入门(作业)
- 第02课 CSS 入门
- 第02课 CSS 入门(作业)
- 第03课 CSS 布局
- 第03课 CSS 布局(作业)
- 步骤2:JavaScript 与前端案例
- 第01课 JavaScript 入门
- 第01课 JavaScript 入门(作业)
- 第02课 仿计算器
- 第03课 前端油画商城案例
- 第04课 轮播图
- 第05课 网页搭建测试
- 第05课 网页搭建测试(含答案)
- 步骤3:JavaScript 教程
- 入门
- 概述
- 基本语法
- 数据类型
- 概述
- 数值
- 字符串
- undefined, null 和布尔值
- 对象
- 函数
- 数组
- 运算符
- 算术运算符
- 比较运算符
- 布尔运算符
- 位运算符
- 运算顺序
- 语法专题
- 数据类型的转换
- 错误处理机制
- 标准库
- String
- Date
- Math
- DOM
- 概述
- Document 节点
- 事件
- EventTarget 接口
- 事件模型
- 常见事件
- 阶段三 数据库开发与实战
- 步骤1:初始数据库操作
- 第01课 数据类型
- 第02课 表的管理
- 第03课 数据管理
- 第04课 常用函数
- 第05课 JDBC 入门
- 第06课 Java 反射
- 第07课 油画商城
- 第08课 数据库基础测试
- 步骤2:MyBatis 从入门到进阶
- 第01课 IntelliJ IDEA 开发工具入门
- 第02课 Maven 入门
- 第03课 工厂模式
- 第04课 MyBatis 入门
- 第05课 MyBatis 进阶
- 第06课 商品信息管理
- 第07课 MyBatis 基础测试
- 步骤3:Redis 数据库与 Linux 下项目部署
- 第01课 Linux 基础
- 第02课 Linux 下 JDK 环境搭建及项目部署
- 第03课 Redis 入门
- 阶段四 SSM 到 Spring Boot 入门与综合实战
- 步骤1:Spring 从入门到进阶
- 第01课 Spring 入门
- 第02课 Spring Bean 管理
- 第03课 Spring AOP
- 第04课 基于 AspectJ 的 AOP 开发
- 第05课 JDBC Template
- 第06课 Spring 事务管理
- 第07课 人员管理系统开发
- 第08课 Spring 从入门到进阶测试
- 步骤2:Spring MVC 入门与 SSM 整合开发
- 第01课 Spring MVC 入门与数据绑定
- 第02课 Restful 风格的应用
- 第03课 SpringMVC 拦截器
- 第04课 办公系统核心模块
- 步骤3:Spring Boot 实战
- 第01课 Spring Boot 入门
- 第02课 校园商铺项目准备
- 第03课 校园商铺店铺管理
- 第04课 校园商铺商品管理及前台展示
- 第05课 校园商铺框架大换血
- 步骤4:Java 面试
- 第01课 面试准备
- 第02课 基础面试技巧
- 第03课 Web基础与数据处理
- 第04课 主流框架