一、异常引入
1.1 两数相除
public class Demo1 {
public static void main(String[] args) {
int i = 10;
Scanner sc = new Scanner(System.in);
int i1 = sc.nextInt(); // 当录入一个非int类型时:java.util.InputMismatchException
System.out.println(i / i1);
// System.out.println(i / 0); // java.lang.ArithmeticException: / by zero
}
}
-
可能会出现的异常
-
当录入的数据应为
int类型,但是录入非int类型数据的时候,出现异常InputMismatchException -
除数为0的时候,出现异常:
ArithmeticException
-
-
异常
Exception:在程序的运行过程中,发生了不正常的现象,阻止了程序的运行,我们称之为异常
1.2 处理方式一:if-else
-
使用
hasNextInt判断录入的是否为整数,不是则打印提示 -
加上
if判断除数是否为0,若除数为0,给出提示 -
代码演示
-
public class Demo1 { public static void main(String[] args) { int i = 10; Scanner sc = new Scanner(System.in); int i1 = sc.nextInt(); // 当录入一个非int类型时:java.util.InputMismatchException System.out.println(i / i1); System.out.println(i / 0); // java.lang.ArithmeticException: / by zero } } -
缺点:
- 代码臃肿,业务代码和处理异常的代码混合在一起
- 可读性差
- 程序员需要花费大量精力来维护这个漏洞
- 程序员很难解决所有的异常
1.3 处理方式二:try…catch…finally
- java提供的异常处理机制
try...catch...finally - 代码演示
public class Demo1 {
public static void main(String[] args) {
int i = 10;
Scanner sc = new Scanner(System.in);
try {
int i1 = sc.nextInt();
System.out.println(i / i1);
}catch (Exception e){
e.printStackTrace();
}
}
}
二、 异常概述
2.1 异常的体系结构
-
异常的概述
- 就是程序出现了不正常的情况。程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。
-
异常的体系结构

Error:严重问题,通过代码无法处理。比如内存溢出Exception:称为异常类,表示程序本身可以处理的问题RuntimeException及其子类:运行时异常。例如空指针异常,数组索引越界异常除RuntimeException之外所有的异常:编译期必须处理的,否则程序不能通过编译。例如日期格式化异常
2.2 编译时异常和运行时异常的区别
-
编译时异常
- 都是
Exception类及其子类 - 必须显示处理,否则程序就会发生错误,无法通过编译
- 都是
-
运行时异常
- 都是
RuntimeException类及其子类 - 无需显示处理,也可以和编译时异常一样处理
- 都是
-
图示

2.3 JVM处理异常的方式
- 异常处理流程
- 首先会看,程序中有没有自己处理异常的代码
- 如果没有,交给本方法的调用者处理(向上传递)
- 如果调用者也没有处理,最终这个异常会交给JVM虚拟机默认处理
- JVM 默认处理的两个步骤:
- 把异常的名称,错误原因及异常出现的位置等信息输出在了控制台
- 程序停止执行
2.4 查看异常信息
- 控制台在打印异常信息:
- 打印异常类名
- 异常出现的原因
- 异常出现的位置
- 我们调bug时,可以根据提示,找到异常出现的位置、分析原因、修改异常代码

三、几种异常的处理方式
3.1 throws方式处理异常
-
定义格式
public void 方法() throws 异常类名 { } -
注意事项
- 这个throws格式是跟在方法的括号后面的
- 编译时异常必须要进行处理,两种处理方案:
try...catch …finally或者throws,如果采用throws这种方案,在方法上进行显示声明,将来谁调用这个方法谁处理 - 运行时异常因为在运行时才会发生,所以在方法后面可以不写,运行时出现异常默认交给
jvm处理

- 运行时异常报错

3.2 throw抛出异常
-
格式:
throw new 异常(); -
注意
这个格式是在方法内的,表示当前代码手动抛出一个异常,下面的代码不用再执行了
-
throws和throw的区别throws throw 用在方法声明后面,跟的是异常类名 用在方法体内,跟的是异常对象名 表示声明异常,调用该方法有可能会出现这样的异常 表示手动抛出异常对象,由方法体内的语句处理 -
还是要调用者处理,不常单独用
3.3 try-catch方式处理异常
-
定义格式
try { // 可能出现异常的代码; } catch(异常类名 变量名) { // 异常的处理代码; } -
执行流程
- 把可能出现异常的代码放入
try代码块中, - 若出现异常,底层会将异常封装为对象,被
catch后面()中的那个异常对象接收,执行对应catch代码块里的内容;(try中出现异常那行后续的代码不执行) - 若无异常或异常不匹配,
catch无法捕获,则catch代码块内容不执行 - 无论是否出现异常,
finally中的代码块都一定会执行 - 异常处理结束后,后面的逻辑代码正常执行
- 把可能出现异常的代码放入
-
示例代码
public class Demo03 {
public static void main(String[] args) {
System.out.println("开始");
method();
System.out.println("结束");
}
public static void method() {
try {
int[] arr = {1, 2, 3};
System.out.println(arr[3]); // 发现异常,直接执行catch块内语句
System.out.println("这里访问不到");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("你访问的数组索引不存在,请回去修改为正确的索引");
}
}
}
- 一些问题
-
如果 try 中没有遇到问题,怎么执行?
- 会把try中所有的代码全部执行完毕,不会执行catch里面的代码
-
如果 try 中遇到了问题,那么 try 下面的代码还会执行吗?
- 那么直接跳转到对应的catch语句中,try下面的代码就不会再执行了
当catch里面的语句全部执行完毕,表示整个体系全部执行完全,继续执行下面的代码
- 那么直接跳转到对应的catch语句中,try下面的代码就不会再执行了
-
如果出现的问题没有被捕获,那么程序如何运行?
- 那么
try...catch就相当于没有写.那么也就是自己没有处理。
默认交给虚拟机处理。
- 那么
-
同时有可能出现多个异常怎么处理?
- 出现多个异常,那么就写多个
catch就可以了.
注:如果多个异常之间存在子父类关系.那么父类一定要写在下面
- 出现多个异常,那么就写多个
3.4 多重catch
try中出现异常以后,将异常的类型跟catch后面的类型依次比较,执行第一个与异常类型匹配的catch语句- 一旦执行其中一条
catch语句以后,后面的catch语句就会被忽略了。所以一般将Exception异常放在最后,用于捕获未知的异常 - 用于对不同的异常分别捕获,使用不同的处理方法。
- 在
JDK1.7以后,异常的新处理方式:可以并列用|符号连接 - 代码示例
public class Demo4 {
public static void main(String[] args) {
int i = 10;
Scanner sc = new Scanner(System.in);
try {
int i1 = sc.nextInt();
System.out.println(i / i1);
}catch (InputMismatchException e){ // 多重catch
e.printStackTrace();
}catch (ArithmeticException e){
e.printStackTrace();
}catch (Exception e){ // 父类写在后面
e.printStackTrace();
}
}
}
3.5 Throwable成员方法
- 在捕获异常
catch后的几种常用处理方法:
| 方法名 | 说明 |
|---|---|
public String getMessage() | 返回此 throwable 的详细消息字符串 |
public String toString() | 返回此可抛出的简短描述 |
public void printStackTrace() | 把异常的错误信息输出在控制台 |
- 示例代码
public class Demo05 {
public static void main(String[] args) {
System.out.println("开始");
method();
System.out.println("结束");
}
public static void method() {
try {
int[] arr = {1, 2, 3};
System.out.println(arr[3]); // 发现异常,直接执行catch块内语句
System.out.println("这里访问不到");
} catch (ArrayIndexOutOfBoundsException e) {
String message = e.getMessage();
System.out.println(message);
String s = e.toString();
System.out.println(s);
e.printStackTrace(); // 不中断程序的运行
}
}
}

3.6 finally的使用
3.6.1 finally介绍
-
引入
- 异常未被捕获、异常被主动
throw抛出或try中遇到return都会导致try...catch后面的代码不执行
- 异常未被捕获、异常被主动
-
如何一定执行后面的代码?
- 将后面的代码用
finally{}代码块包裹起来,则一定会执行 - 只有
System.exit(0)会使finally中的代码不执行
- 将后面的代码用
-
示例代码
public class Demo6 {
public static void main(String[] args) {
try {
System.out.println("try...");
// System.exit(0); 强制退出虚拟机,finally...不打印
return;
}catch (Exception e){
}finally {
System.out.println("finally...");
}
System.out.println("后续代码"); // 代码不运行
}
}
return和finally的执行顺序(☆)- 先执行
finally后直接return
- 先执行
3.6.2 什么样的代码会放在finally中呢?
- 关闭数据库资源
- 关闭IO流资源
- 关闭socket资源
- 等需要系统调用的资源
3.7 异常的练习 (应用)
-
需求
键盘录入学生的姓名和年龄,其中年龄为18 - 25岁,超出这个范围是异常数据不能赋值.需要重新录入,一直录到正确为止
-
实现步骤
- 创建学生对象
- 键盘录入姓名和年龄,并赋值给学生对象
- 如果是非法数据就再次录入
-
代码实现
学生类
public class Student {
private int age;
private String name;
public Student() {
}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age >= 18 && age <= 25) {
this.age = age;
} else {
throw new RuntimeException("年龄不合法");
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
- 测试类
import java.util.InputMismatchException;
import java.util.Scanner;
public class TestStudent {
public static void main(String[] args) {
Student student = new Student();
Scanner sc = new Scanner(System.in);
System.out.println("请输入学生的姓名");
String name = sc.next();
student.setName(name);
while (true) {
System.out.println("请输入学生年龄");
int age;
try {
age = sc.nextInt();
} catch (InputMismatchException e) {
System.out.println(e.toString());
System.out.println("请输入一个整数");
sc.nextLine(); // 缓存区还存在非int值,先接受掉
continue;
}
try {
student.setAge(age);
break;
} catch (RuntimeException e) {
System.out.println(e.toString());
System.out.println("请重新输入");
continue;
}
}
String s = student.toString();
System.out.println(s);
}
}
- 运行结果

3.8 自定义异常
-
自定义异常概述
当Java中提供的异常不能满足我们的需求时,我们可以自定义异常
-
实现步骤(继承后提供两个构造方法)
- 定义异常类
- 写继承关系
- 提供空参构造
- 提供带参构造
-
代码实现
异常类
public class AgeOutOfBoundsException extends RuntimeException {
public AgeOutOfBoundsException() {
}
public AgeOutOfBoundsException(String message) {
super(message);
}
}
- 学生类
...
public void setAge(int age) {
if(age >= 18 && age <= 25){
this.age = age;
}else{
//如果Java中提供的异常不能满足我们的需求,我们可以使用自定义的异常
throw new AgeOutOfBoundsException("年龄超出了范围");
}
}
...
本文详细介绍了Java中的异常处理,包括异常的类型、处理方式以及最佳实践。讲解了try-catch-finally、throws、throw关键字的使用,并提供了异常处理的示例代码。强调了在处理异常时避免代码臃肿,提高可读性和可维护性的重要性。同时,通过实例展示了如何处理用户输入的异常数据,确保程序的稳定运行。最后,提到了自定义异常的创建,以满足特定场景下的异常处理需求。
1016

被折叠的 条评论
为什么被折叠?



