异常的概念及分类
什么是异常
异常就是程序出现了问题。
以下程序哪里出了问题?
1
2
|
int[] arr = {10, 20, 30};
System.out.println(arr[3]);
|
1
2
3
4
5
|
String teachers[] = {"张三", "李四", "王五"};
for (int i = 0; i < 4; i++) {
System.out.println(teachers[i]);
}
System.out.println("显示完毕!");
|
Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
at com.company.expection.Demo1.main(Demo1.java:6)
其中 ArrayIndexOutOfBoundsException
表示:数组索引越界异常。
1
2
3
|
int a = 10;
int b = 0;
System.out.println(a / b);
|
Exception in thread “main” java.lang.ArithmeticException: / by zero at com.company.expection.Demo1 .main(Demo1.java:7)
其中 ArithmeticException
表示:算数异常。
异常结构
异常基类
所有异常都继承自 java.lang.Throwable 类
Throwable 类有两个直接子类:Error
类和 Exception
类
系统级错误
Error 是程序无法处理的错误,表示运行应用程序中较严重问题。
大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM 出现的问题。
-
Virtual MachineError
- Java 虚拟机运行错误
-
NoClassDefFoundError
- 类定义错误
-
OutOfMemoryError
- 内存溢出错误
异常
运行时异常
NullPointerException
- 空指针引用异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public String[] Arr = {"11", "22", null};
public static void main(String[] args) {
try {
Demo9 demo = new Demo9();
demo.test(0);
demo.test(2);
} catch (NullPointerException e) {
System.out.println("空指针异常");
}
}
public void test(int index) throws NullPointerException {
System.out.println("Arr[" + index + "]长度:" + Arr[index].length());
}
|
ArithmeticException
- 算术运算异常
1
2
3
|
int a = 10;
int b = 0;
System.out.println(a / b);
|
编译时异常
ParseException
- 数据格式转换异常
1
2
3
4
|
String time = "2023年10月31日 08点30分33秒";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH点mm分ss秒");
Date date = sdf.parse(time);
System.out.println(date);
|
SQLException
- 数据库操作异常
FileNotFoundException
- 文件找不到的异常
1
2
|
FileInputStream fis = new FileInputStream("a.txt");
fis.read();
|
TimeoutException
- 执行超时异常
编译时异常和运行时异常
编译时异常:
提醒程序员检查本地信息,如果有问题就会出这个异常。
在编译阶段,Java 不会运行代码,只会检查语法是否错误,或者做一些性能优化。
如 int a = 2.2 ;
运行时异常:
代码出错导致程序出现的问题。
异常的作用
1
2
|
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
at com.company.exception.Demo8.main(Demo8.java:11)
|
- 异常可以做为方法内部的一个特殊返回值,以便于通知调用者底层的执行情况
1
2
3
4
5
6
7
8
9
10
11
|
public static void main(String[] args){
try {
int[] arr = {10, 20, 30};
System.out.println(arr[3]);
}catch (Exception e){
System.out.println("程序出现了异常!");
System.out.println("异常名为:"+e);
System.out.println("异常提示信息为:"+e.getMessage());
System.out.println("异常堆栈信息为:");
e.printStackTrace();
}
|
异常的捕获及异常处理
捕获异常
如果自己可以处理异常,使用 try {…} catch (…) {…} finally 结构包裹可能有异常的代码
与 if (…) {…} else if (…) {…} else {…}结构类似,但使用异常处理比选择结构更方便
如果代码正常,则正常执行,如果有异常,则进入catch
1
2
3
4
5
6
7
8
9
|
try{
// 如果代码正常,执行这段代码
}catch (Exception1 e){
// 如果代码有异常1,执行这段代码
}catch (Exception2 e){
// 如果代码有异常2,执行这段代码
}finally {
// 是否发生异常都会执行这段代码
}
|
断点调试,分析程序运行顺序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo1 {
public static void main(String[] args) {
try{
// 如果代码正常,执行这段代码
// 时间格式转换
String time = "2023年10月31日 08点26分34秒";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH点mm分ss秒");
Date date = sdf.parse(time);
System.out.println(date);
// 数学计算
int a = 10;
int b = 0;
System.out.println(a / b);
}catch (ParseException e){ // 如果代码有异常1,执行这段代码
System.out.println("转换异常");
}catch (ArithmeticException e){ // 如果代码有异常2,执行这段代码
System.out.println("算数异常");
}catch (Exception e){ // 如果代码有其他异常,执行这段代码
System.out.println("其他异常");
System.out.println(e);
// 打印异常的堆栈信息 e.printStackTrace();
// 打印错误位置 e.getMessage();
}finally { // 是否发生异常都会执行这段代码
System.out.println("默认执行");
}
}
}
|
如果 try 中代码发生异常,则不再执行 try 中异常后的代码,直接跳到 catch 中执行
不管是否有异常都执行 finally,即使遇到 return,也执行 finally
只有虚拟机关闭,才不执行 finally,System.exit(1);
练习:使用 Scanner 声明两个 int 类型的变量,但在录入时故意输入其他类型,使用 try … catch 语句捕获异常并输出异常名。
抛出异常
如果自己无法处理,则使用 throws 关键字,将异常抛出给上一级处理
使用 throws 关键字包裹可能出现异常的代码, 用来声明异常
- 如果一个方法内部调用其他方法,其他方法抛出一个编译/运行时异常,该方法不想处理,可以使用 throw 异常类,继续把异常往上抛
1
2
3
4
5
6
7
8
9
|
public static void main(String[] args) throws Exception{
test1();
}
public static void test1() throws ParseException, ArithmeticException { // 向上抛出异常
String time = "2023年10月31日 08点26分34秒";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH点mm分ss秒");
Date date = sdf.parse(time);
System.out.println(date);
}
|
-
如果抛出的是 编译 时异常,必须在方法标签上,使用 throws 声明抛出什么类型异常
-
如果抛出的是 运行 时异常,可以在方法标签上声明或者不声明
-
如果异常类型,有父子关系,只需要写父类异常类型
-
throws后面可以接多个异常类,多个异常类使用逗号分隔
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
package com.company.exception;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.InputMismatchException;
import java.util.Scanner;
public class Demo2 {
public static void main(String[] args) throws Exception { // 继续往上抛出异常(JVM)
test1(); // 编译时异常(检查异常)
try {
test2();
} catch (ArithmeticException e) {
System.out.println("算数异常");
}
try {
test3();
} catch (InputMismatchException e) {
System.out.println("输入类型不匹配异常");
}
}
public static void test1() throws ParseException, ArithmeticException { // 向上抛出异常
String time = "2023年10月31日 08点26分34秒";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH点mm分ss秒");
Date date = sdf.parse(time);
System.out.println(date);
}
public static void test2() throws ArithmeticException { // 向上抛出异常
int a = 10;
int b = 0;
System.out.println(a / b);
}
public static void test3() throws InputMismatchException {
Scanner sc = new Scanner(System.in);
int num1 = 0;
int num2 = 0;
System.out.print("请输入num1:");
num1 = sc.nextInt();
System.out.print("请输入num2:");
num2 = sc.nextInt();
System.out.println("num1+num2的值为:" + (num1 + num2));
}
}
|
使用 throw 关键字,抛出自定义异常
jdk 中自带了很多类型的异常,但如果这些内置的异常仍然不能满足项目需求,就需要创建自定义异常
如何自定义异常?
- 任意异常都继承自 Exception,所以需要自定义一个类继承自 Exception
1
2
3
4
5
|
public class MyException extends Exception { // 继承自Exception
public MyException(String message) {
super(message); // 把异常信息传到父类里
}
}
|
-
使用throw声明一个自定义异常,再使用 try…catch 或 throws 处理
// 键盘录入两个整数,a,b,如果 a+b > 30 且 a+b < 100
// 输出 运行成功,并输出 a 和 b 的值,
// 自定义异常,如果出现异常,提示 取值范围错误
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public class Demo5 {
public static void main(String[] args) {
int age = -20; // 年龄不能小于等于0
if (age<=0){
try {
// 首先声明一个异常,再使用 try...catch 或 throws 检查是否有异常
throw new MyException("年龄不能小于等于0");
// throws NullPointerException
}catch (MyException e){
e.printStackTrace();
// System.out.println(e.getMessage());
}
}
}
}
|
常见的异常
RuntimeException
NullPointerException —— 空指针引用异常
ArithmeticException —— 算术运算异常
ArrayIndexOutOfBoundsException —— 数组索引越界异常
InputMismatchException —— 输入类型不匹配异常
ClassCastException —— 类转换异常
IllegalArgumentException —— 非法参数异常
OtherException
ParseException —— 数据格式转换异常
SQLException —— 数据库操作异常
IOException —— 输入输出异常
FileNotFoundException —— 文件找不到的异常
TimeoutException —— 执行超时异常
IllegalAccessException —— 访问权限异常
练习
-
下列关于异常的描述,哪一项是错误的( )?
-
A. 异常可以用 try {…} catch (Exception e) { … } 来捕获并进行处理。
-
B. 异常的基类为 Throwable,所有异常都必须直接或者间接继承它。
-
C. 如果某异常类继承自 RuntimeException,则该异常可以不显式的使用 “ try … catch … ” 或 throws 进行处理。
-
D. 所有的异常,在语法上都必须用 throws 或者 try {…} catch {…} 处理。
-
关于关于异常的含义,下列描述中最正确的一个是( )。
-
下列关于异常的描述,哪一项是错误的( )?
-
A. 异常的基类是 Exception。
-
B. 程序员通常不用处理 Error 类型的异常。
-
C. 在使用 catch 捕获异常时,需要先捕获小范围异常,再捕获大范围异常。
-
D. 对于可能发生的异常,可以使用 throws 来声明以提示调用者进行处理,或者使用 catch 捕获并建立异常处理的逻辑。
-
以下哪个是检查异常( )?
-
下列( )异常表示向方法传递了一个不合法或不正确的参数。
-
A. IllegalAccessException
-
B. IllegalArgumentException
-
C. ClassCastException
-
D. InputMismatchException
-
以下描述错误的是哪个?
-
A. throw 表示方法内抛出某种异常对象。
-
B. throw 抛出的异常对象是非运行时对象,则需要在方法申明时加上该异常的抛出,即需要加上 throws 语句或者在方法体内用 try…catch… 处理该异常,否则编译报错。
-
C. 执行到 throw 语句则后面的语句块不再执行。
-
D. 方法的定义上使用 throws 表示这个方法可能抛出某种异常,方法的调用者不必进行异常处理。
-
以下程序的执行结果是什么?
1
2
3
4
5
6
7
8
9
|
int[] myArray = new int[3];
try {
for (int i = 0; i <= myArray.length; i++) {
myArray[i] = i * 3;
System.out.println("myArray 数组的第" + i + "个元素的值是" + myArray[i]);
}
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组下标越界");
}
|