异常
第十二章 异常
异常基本介绍
Java 语言中,将程序执行中发生的不正常行为称为“异常”。(开发过程中语法错误和逻辑错误不是异常)
执行过程中所发生的异常事件可以分为两大类:
- Error(错误) : Java虚拟机无法解决的严重问题。如 : JVM系统内部错误、资源耗尽等严重情况。比如 : StackOverflowError[栈溢出]和 OOM(out of memory),Error 是严重错误,程序会崩溃。
- Exception : 其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如空指针访问、试图读取不存在的文件、网络连接中断等等。Exception 分为两大类 : 运行时异常(程序运行时发生的异常)和编译时异常(编程时,编译器检查出的异常)。
★异常体系图

- 异常分为两大类:
- 运行时异常(RuntimeException):编译器不要求强制处置的异常,一般是指编程时的逻辑错误,是程序员应该避免其出现的异常。
java.lang.RuntimeException
类及它的子类都是运行时异常。 - 编译时异常:是编译器要求必须处理的异常。
- 运行时异常(RuntimeException):编译器不要求强制处置的异常,一般是指编程时的逻辑错误,是程序员应该避免其出现的异常。
五大运行时异常
NullPointerException
- 空指针异常:当应用程序试图在需要对象的地方使用 null 时,抛出该异常。
ArithmeticException
- 数学运算异常:当出现异常的运算条件时,抛出该异常,例如,除数为 0 时。
ArrayIndexOutOfBoundsException
- 数组下标越界异常:用非法索引访问数组时抛出的异常,如果索引为负或大于等于数组大小,则该索引为非法索引。
ClassCastException
类型转换异常:当试图将对象强制转换为不是实例的子类时,抛出该异常。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package com.f.chapter12.exception_;
public class ClassCastException_ {
public static void main(String[] args) {
A a = new B(); //父类的变量指向子类的对象 -> 向上转型
B b = (B)a; //向下转型
//C c = (B)a; //ClassCastException
}
}
class A {
}
class B extends A {
}
class C extends A {
}
NumberFormatException
数字格式不正确异常:当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常 => 使用该异常我们可以确保输入的是满足条件的数字。
1
2
3
4
5
6
7
8
9
10
11package com.f.chapter12.exception_;
public class NumberFormatException {
public static void main(String[] args) {
String s1, s2;
s1 = "1234";
s2 = "comf";
System.out.println(Integer.parseInt(s1)); //输出1234
System.out.println(Integer.parseInt(s2)); //异常:NumberFormatException
}
}
★异常处理机制
异常处理就是当异常发生时,对异常处理的方式。
try-catch-finally
:程序员在代码中捕获发生的异常,自行处理。1
2
3
4
5
6
7
8
9
10try{
//可能有异常的代码
}catch(Exception e){
//捕获到异常
//当异常发生时,系统将异常封装成Exception对象e,传递给catch,得到异常对象后,程序员自己处理
//注意:如果没有发生异常,catch代码块不执行
}finally{
//不论try代码块是否发生异常,都会执行finally代码块
//所以通常将释放资源的代码放在finally里
}- 注:在 IDEA 中,可以将想要
try-catch-finally
的代码块选中,然后按快捷键ctrl+alt+t
来快捷添加try-catch-finally
。
- 注:在 IDEA 中,可以将想要
throws
:将发生的异常抛出,交给调度者(方法)来处理,最顶级的处理者就是 JVM(输出异常信息,退出程序)。
注意:
try-catch-finally
和throws
二选一,不要一起用。如果程序员既没有用try-catch-finally
、也没有用throws
,则默认使用throws
。
try-catch
Java 提供 try 和 catch 块来处理异常。try 块用于包含可能出错的代码,catch 块用于处理 try 块中发生的异常。可以根据需要在程序中有多个 try…catch 块。
基本语法:
1
2
3
4
5
6
7try{
//可疑代码
//将异常生成的对应的异常对象,传递给catch块
}catch(异常){
//对异常的处理
}
//如果没有finally,语法可以通过try-catch
注意事项:如果异常发生了,则异常发生后面的代码不会执行,直接进入到
catch
块;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16package com.f.chapter12.try_;
public class TryCatchDetail {
public static void main(String[] args) {
try {
//用ctrl+alt+t快捷键快速添加try-catch块
String s = "com";
int num = Integer.parseInt(s);
//如果异常发生了,则异常发生后面的代码不会执行,直接进入到catch块
System.out.println("数字 = " + num);
} catch (NumberFormatException e) {
System.out.println("异常信息:" + e.getMessage());
}
System.out.println("程序继续执行...");
}
}输出结果为:
1
2
3
4异常信息:For input string: "com"
程序继续执行...
进程已结束,退出代码0如果异常没有发生,则顺序执行
try
的代码块,不会进入到catch
;如果希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等),则使用
finally
;可以有多个
catch
语句,捕获不同的异常(进行不同的业务处理),要求父类异常在后,子类异常在前(否则会报错),比如 Exception 在后,NullPointerException 在前,**如果发生异常,只会匹配一个catch
**。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20package com.f.chapter12.try_;
public class TryCatchDetail2 {
public static void main(String[] args) {
try{
String s = null;
System.out.println(s.length());
int i = 10 / 0;
}catch(NullPointerException e){
System.out.println("空指针异常...");
System.out.println("错误原因为:" + e.getMessage());
}catch(Exception e){
System.out.println("其他异常...");
System.out.println("错误原因为:" + e.getMessage());
}finally{
System.out.println("不论是否发生异常,一定会执行......");
}
System.out.println("程序继续执行...");
}
}输出结果为:
1
2
3
4空指针异常...
错误原因为:null
不论是否发生异常,一定会执行......
程序继续执行...可以进行
try-finally
配合使用,这种用法相当于没有捕获异常,因此如果发生了异常,则程序在执行完finally
块后会直接崩掉。应用场景:执行一段代码之后,不管是否发生异常,都必须执行某个业务逻辑。
1
2
3
4
5
6
7
8
9
10
11
12
13package com.f.chapter12.try_;
public class TryCatchDetail3 {
public static void main(String[] args) {
try{
String s = null;
System.out.println(s.length());
}finally{
System.out.println("不论是否发生异常,一定会执行......");
}
System.out.println("程序继续执行...");
}
}输出结果为:
1
2
3
4
5不论是否发生异常,一定会执行......
Exception in thread "main" java.lang.NullPointerException
at com.f.chapter12.try_.TryCatchDetail3.main(TryCatchDetail3.java:7)
进程已结束,退出代码1finally
块中的代码是一定要执行的,即使在try
块或catch
块中有return
语句,也还是会执行finally
块中的代码,可以理解为finally
的优先级要高于return
。
try-catch-finally
执行顺序小结:- 如果没有出现异常,则执行
try
块中所有语句,不执行catch
块中语句,如果有finally
,最后还需要执行finally
里面的语句; - 如果出现异常,则
try
块中异常发生后,try
块剩下的语句不再执行,将执行catch
块中的语句,如果有finally
,最后还需要执行finally
里面的语句。
- 如果没有出现异常,则执行
throws
如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。
在方法声明中用
throws
语句可以声明抛出异常的列表,throws
后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。1
2
3
4
5
6
7
8
9
10
11package com.f.chapter12.throws_;
public class Throws01 {
public static void main(String[] args) {
}
public void f1() throws NullPointerException, ArithmeticException, NumberFormatException{
//f1抛出了三种运行时异常
}
}throws
注意事项:- 对于编译异常,程序中必须处理,比如
try-catch
或者throws
; - 对于运行时异常,程序中如果没有处理,默认就是
throws
的方式处理;- 在实际开发中,我们可以这样处理:将编译异常用运行时异常抛出,这时调用者可以选择捕获该异常,也可以选择默认处理该异常,比较方便。
- 子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常类型的子类型;
- 在
throws
过程中,如果有方法try-catch
,就相当于处理异常,就可以不必throws
。
- 对于编译异常,程序中必须处理,比如
自定义异常
当程序中出现了某些“错误”,但该错误信息并没有在 Throwable 子类中描述处理,这个时候可以自己设计异常类,用于描述该错误信息。
自定义异常的步骤:
- 定义类:自定义异常类名(程序员自己写),继承 Exception 或 RuntimeException;
- 如果继承 Exception,则属于编译异常;
- 如果继承 RuntimeException,则属于运行异常(一般来说,继承 RuntimeException,以便于使用默认的
throws
处理方式)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package com.f.chapter12.customexception_;
public class CustomException {
public static void main(String[] args) {
int age = 10;
//要求年龄范围在18-120之间,否则抛出一个自定义异常
if (!(age >= 18 && age <= 120)) {
throw new AgeException("年龄需要设置在18-120之间...");
}
System.out.println("年龄范围正确...");
}
}
class AgeException extends RuntimeException {
public AgeException(String message) {
super(message);
}
}
throw和throws
意义 | 位置 | 后面跟的东西 | |
---|---|---|---|
throws | 异常处理的一种方式 | 方法声明处 | 异常类型 |
throw | 手动生成异常对象的关键字 | 方法体中 | 异常对象 |