异常的处理机制可以确保我们程序的健壮性,提高系统可用率
基本概念
定义
异常情形是指阻止当前方法或者作用域继续执行的问题
Java 中采用“类”去模拟异常,类可以创建对象1
2
3
4
5NullPointerException e = 0x1234;
e 是引用类型, e 中保存的内存地址指向堆中的“对象”
这个对象一定是 NullPointerException 类型
这个对象就表示真实存在的异常事件
NullPointerException 则表示一类异常
出现异常的情况:只有在当前的环境下程序无法正常运行,这时它就会从当前环境中跳出,并抛出异常
- 用户输入了非法数据
- 要打开的文件不存在
- 网络通信时连接中断,或者 JVM 内存溢出
异常处理机制的作用
程序发生异常事件之后,为我们输出详细的信息,程序员通过这个信息,可以对程序进行处理,使程序更加健壮
eg. JVM 自动创建 ArithMeticException 类型的对象包含了对象的详细信息1
2
3
4
5
6
7
8
9
10
11public class ExceptionTest01 {
public static void main(String[] args) {
int a = 10;
int b = 0;
int c = a/b; //JVM 自动创建了对象
//ArithMeticException e = 0x2356;
}
}
异常的用途
1 | public class Calculator { |
以上程序编译通过了,但是运行时出现了异常,表示发生某个异常事件
JVM 向控制台输出了如下信息:1
2Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.day032801.ExceptionTest01.main(ExceptionTest01.java:10)
本质:程序执行过程中发生了算数异常事件,JVM 为我们创建了一个 ArithMeticException 类型的对象
并且这个对象中包含了详细的异常信息,并且 JVM 将这个对象中的信息输出到控制台
异常的体系结构
通过以上示例,可以看到 Java 给我们提供了这样的体系结构:当出现问题的时候,它会告诉我们,并且把错误的详细信息也告诉我们了,这就是异常的体系结构,这样我们的程序就会更健壮,我们可以把这个信息再进行处理一下,告诉用户。从上面大家还可以看到,Java 异常都是类,在异常对象中会携带一些信息给我们,我们可以通过这个异常对象把信息取出来
异常类的层次结构
常见的异常类型
常见异常类型的处理方案
1 | 1. java.lang.NullPointerException(空指针异常) |
空指针异常发生在对象为空,但是引用了这个对象的方法1
2String s = null;//此时 s 为空
int length = s.length();//发生空指针异常
1 | 2. java.lang.ClassNotFoundException(指定的类型不存在) |
检查类的名称和路径是否正确,比如调用 Class.forName() ,类的名称不正确
1 | 3.java.lang.NumberFormatException(字符串转换为数字异常) |
当试图将一个 String 转换为指定的数字类型,而该字符串却不满足数字类型要求的格式时,抛出该异常
如现在讲字符型的数据 123456 转换为数值型数据时,是允许的
但是如果字符型数据中包含了非数字型的字符,如 123#56 ,此时转换为数值型时就会出现异常。系统就会捕捉到这个异常
1 | 4.java.lang.IndexOutOfBoundsException(数组下标越界异常) |
调用数组超过数组的长度,最好先查看一下数组的length,以免出现这个异常
1 | 5.java.lang.IllegalArgumentException(方法的参数错误) |
比如 g.setColor(int red,int green,int blue) 这个方法中的三个值,如果有超过 255 的也会出现这个异常,因此一旦发现这个异常,我们要做的,就是赶紧去检查一下方法调用中的参数传递是不是出现了错误
1 | 6.java.lang.IllegalAccessException(没有访问权限) |
当应用程序要调用一个类,但当前的方法即没有对该类的访问权限便会出现这个异常。对程序中用了 Package 的情况下要注意这个异常
1 | 7.java.lang.ArithmeticException(数学运算异常) |
当算术运算中出现了除以零这样的运算就会出这样的异常
1 | 8.java.lang.ClassCastException(数据类型转换异常) |
当试图将对某个对象强制执行向下转型,但该对象又不可转换为其子类的实例时将引发该异常
1 | 9.java.lang.FileNotFoundException(文件未找到异常) |
当程序试图打开一个不存在的文件进行读写时将会引发该异常。该异常由 FileInputStream,FileOutputStream,RandomAccessFile 的构造器声明抛出
即使被操作的文件存在,但是由于某些原因不可访问,比如打开一个只读文件进行写入,这些构造方法仍然会引发异常
1 | 10.java.lang.NoSuchMethodException(方法不存在异常) |
当程序试图访问(修改或读取)某个方法,但是该方法不存在就会引发异常
1 | 11.java.lang.InstantiationException(实例化异常) |
当试图通过 Class 的 newInstance() 方法创建某个类的实例,但程序无法通过该构造器来创建该对象时引发
- Class 对象表示一个抽象类,接口,数组类,基本类型
- 该 Class 表示的类没有对应的构造器
1 | 12.java.lang.OutOfMemoryException(内存不足错误) |
当可用内存不足以让 Java 虚拟机分配给一个对象时抛出该错误
1 | 13.java.lang.NoClassDefFoundException(未找到类定义错误) |
当 Java 虚拟机或者类装载器试图实例化某个类,而找不到该类的定义时抛出该错误1
2
3
4
5
6
714 违背安全原则异常:SecturityException
15 操作数据库异常:SQLException
16 输入输出异常:IOException
17 通信异常:SocketException
异常处理机制的主要组成
1 | try:监控有可能产生异常的语句块 |
throw\throws
1 | public static void main(String[] args) throws Exception { |
try…catch…finally
1 | try{ |
catch 语块可以写多个,但是必须从上到下,从小到大(子类 –> 父类)
深入 throws
1 | import java.io.*; |
输出结果:1
2
3
4
5
6
7
8
9Exception in thread "main" java.io.FileNotFoundException: c:\ab.txt (系统找不到指定的文件。)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
at com.day032801.ExceptionTest03.m3(ExceptionTest03.java:20)
at com.day032801.ExceptionTest03.m2(ExceptionTest03.java:16)
at com.day032801.ExceptionTest03.m1(ExceptionTest03.java:12)
at com.day032801.ExceptionTest03.main(ExceptionTest03.java:6)
使用 throws 处理异常不是真正处理异常而是推卸责任,谁调用的就会抛给谁
因为 m1 方法出现异常采用的是上抛,给了 JVM , JVM 遇到这个异常就会退出 JVM ,下面的程序不会执行
所以不会输出 Wihieree
出现上面输出结果的原因是:1
2
3在程序运行过程中发生了 FileNotFoundException 类型的异常
JVM 为我们创建了一个 FileNotFoundException 类型的对象
该对象中携带上面的信息,JVM 负责将该对象的信息打印到控制台,并且 JVM 停掉了程序的运行
深入 try…catch…
1 | import java.io.FileInputStream; |
getMessage() 和 printStackTrace() 方法
printStackTrace() 输出信息更详细,更适合用于调试程序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
28import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class ExceptionTest04 {
public static void main(String[] args) {
try{
FileInputStream fis = new FileInputStream("abc");
}catch(FileNotFoundException e){
//打印异常堆栈信息
//一般情况下都会使用该方式去调试程序
e.printStackTrace();
/*
java.io.FileNotFoundException: abc (系统找不到指定的文件。)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
at com.day032801.ExceptionTest04.main(ExceptionTest04.java:9)
*/
String msg = e.getMessage();
System.out.println(msg); //abc (系统找不到指定的文件。)上面方法的简略
}
//这段代码会执行
System.out.println("WiHieree");//WiHieree
}
}
finally 关键字
finally 语句块可以直接和 try 语句块联用 try…finally…
try…catch…finally 也可以
在 finally 语句块中的代码是一定会执行的,但只要在 finally 之前退出了 JVM,finally 语句就不会执行
eg. System.exit(0);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//深入 finally 语句块
public class ExceptionTest05 {
public static void main(String[] args) {
int i = m1();
System.out.println("main 的 i --> " + i);//10
}
public static int m1(){
int i = 10;
try{
return i; //10
}finally {
i++;
System.out.println("finally 的 i --> " + i);//11
}
}
}
//输出结果
finally 的 i --> 11
main 的 i --> 10
注意 mian 方法返回的 i 是 10,接受的是 try 里 return 的 i
try…catch…finally
finally 语句块是一定会执行的,所以通常在程序中
为了保证某资源一定会释放,所以一般在 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
26import java.io.*;
public class ExceptionTest06 {
public static void main(String[] args) {
//必须在外面声明
FileInputStream fis = null;
try{
fis = new FileInputStream("E:/theme-66.epf");
}catch(FileNotFoundException e){
e.printStackTrace();
}finally {
//避免空指针异常,保证资源一定会释放
if (fis!=null){
try{
fis.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
}
}
//输出结果
输出结果为空,因为没有异常,在 finally 已经释放资源
自定义异常
异常的完整应用,其中包含手动抛出异常
eg. 需求自定义注册“无效名字异常”1
2
3
4
5
6
7
8
9
10
11//自定义一个异常
public class IllegalNameException extends Exception{//编译时异常
//public class IllegalNameException extends Exception{//运行时异常
//定义异常一般提供两个构造方法
public IllegalNameException(){}
public IllegalNameException(String msg){
super(msg);
}
}
1 | //顾客相关的业务 |
1 | //模拟注册 |
方法的重写与异常
重写的方法不能比被重写的方法抛出更宽泛的异常1
2
3
4
5
6
7
8
9class A{
public void m1(){}
}
class B extends A{
//子类永远无法抛出比父类更多的异常
public void m1() throws Exception{}
}
以上编译不通过1
2
3
4
5
6
7
8
9
10
11import java.io.FileNotFoundException;
import java.io.IOException;
class A{
public void m1() throws IOException {}
}
class B extends A{
public void m1() throws FileNotFoundException{}
}
以上程序编译通过