一、首先体验程序中的异常?
1.什么是异常?
- 程序在运行中发生了意外的情况,称为异常(Exception),程序一旦出现异常,后面的代码将无法执行,程序终止
- 为了保证后面的代码正常执行,需要对异常进行处理
- java中采用“类”去模拟异常
- 类是可以创建对象的
- 比如
NullPointerException e = 0x1234;
表示e是引用类型,e中保存的内存地址指向堆中的“对象”,这个对象一定是NullPointerException类型 - 这个对象就表示真实存在的异常事件
- NullPointerException是一类异常(不同的异常对应不同的类)
- 比如
2.异常小案例
package 异常机制;
public class Test {
public static void main(String[] args) {
int a = 10;
int b = 0;
int c = a/b;
//第8行的代码出现了异常,“没有处理”,下面的代码不会执行,直接退出了JVM
System.out.println("Hello World");
}
}
- 控制台输出结果
Exception in thread "main" java.lang.ArithmeticException: / by zero
at 异常机制.Test.main(Test.java:8)
- 上面代码,可以看出,以上程序编译通过了,但是运行时出现了异常,表示发生某个异常事件
- JVM向控制台输出如下的信息,本质是:程序执行过程中发生了算数异常这个事件(因为被除数不能为0),JVM为我们创建了一个ArithmeticException类型的对象。并且这个对象中包含了详细的异常信息,并且JVM将这个对象中的信息输出到控制台
- 由于出现算数异常,又没有对其进行处理,所以Hello World不会输出
- 如果不懂上面异常的本质,可以先学习理解一下类与对象的创建与使用]
3.异常机制的作用?
- java语言为我们提供一种完善的异常处理机制
- 程序发生异常事件之后,为我们输出详细的信息,程序员通过这个信息,可以对程序进行一些处理,使程序更加健壮
二、异常的层次结构
- 上诉异常处理机制图示结构:
Object
Throwable
Exception
编译时异常
运行时异常
Error
- Exception
- 编译时异常:
- 所有Exception的直接子类都是“编译时异常”
- 该异常是可预期,很有可能发生的,发生的机率比较高
- 所有的编译时异常,要求程序员在编写程序阶段,必须对它进行处理,如果不处理的话,编译无法通过。处理异常有两种方式:捕捉和声明抛出。捕捉:try…catch…,声明抛出就是在方法声明的位置上使用throws关键字抛出异常
- 运行时异常:
- 所有RuntimeException的子类都是运行时异常
- 该类异常不一定发生,发生的机率比较低,如果代码没有出现逻辑错误,是不会发生
- 编译器不要求去处理,程序员在写代码时可以不处理
- 编译时异常:
- Error
- java程序运行过程中如果出现了错误,属于JVM和系统发生的错误,程序员无法解决的问题。例如:StackOverFlowError (一旦线程栈的大小增长超过了允许的内存限制,就会抛出 java.lang.StackOverflowError 错误)
二、处理异常的两种方式
- 使用throws 抛出异常
- 使用try…catch处理异常
1.使用throws 抛出异常
1.1 首先看一下要求异常处理的代码
package 异常机制;
import java.io.FileInputStream;
public class Test02 {
public static void main(String[] args) {
m1();
}
public static void m1() {
m2();
}
private static void m2() {
m3();
}
private static void m3() {
new FileInputStream("D:/x.txt");
}
}
-
以上程序会发现FileInputStream在编译的时候会报红色波浪线
-
看源码发现FileInputStream这个构造方法在声明的位置上使用了throws FileNotFoundException(向上抛)
-
FileNotFoundException继承Exception类,所以属于编译时异常,要求程序员必须对其进行异常处理(throws抛出或者try…caych)
1.2 对上述代码使用throws抛出异常
在方法声明的位置上使用throws FileNotFoundException就可以编译通过了,或者写IOException,或者写Exception,写比FileNotFoundException更宽泛的异常就可以
package 异常机制;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class ExceptionTest02 {
public static void main(String[] args) throws FileNotFoundException{
m1();
//使用throws处理异常不是真正处理异常而是推卸责任
//谁调用的就会抛给谁
//上面的m1方法如果出现了异常,因为采用的是上抛,给了JVM,JVM遇到这个异常就会退出JVM,下面的这个代码不会执行
System.out.println("Hello World!");
}
public static void m1() throws FileNotFoundException{
m2();
}
private static void m2() throws FileNotFoundException{
m3();
}
private static void m3() throws FileNotFoundException {
new FileInputStream("D:/x.txt");
}
}
- 上诉程序可以看出,从调用m3方法开始一直向上抛异常,直到m1方法出现了异常,并且采用的是上抛,再抛给main方法,最终抛给了JVM(java虚拟机),JVM遇到这个异常就会退出JVM,下面的"Hello World!"字符串字样就不会打印输出
- 上述程序编译没有问题,打印输出信息如下
- 可以看出在程序运行过程中发生了FileNotFoundException类型的异常
- JVM为我们创建了一个FileNotFoundException类型的对象
- 该对象中携带以下的信息
- JVM负责将该对象的信息打印到控制台
- 并且JVM停掉了程序的运行
如果对m1方法进行try…catch进行捕捉
-
打印输出:
-
可以看出,如果对m1方法进行try…catch处理,捕捉到了这个异常并且处理了这个异常,那么"Hello World!"就会打印输出
1.3 总结
- 使用throws处理异常不是真正处理异常而是推卸责任,如果想要处理该异常,使用try…catch进行捕捉,就不会抛给JVM而使程序异常退出
2.使用try…catch处理异常
2.1 语法
try{
可能出现异常的代码;
}catch(异常类型1 变量){
处理异常的代码;
}catch(异常类型2 变量){
处理异常的代码;
}...
- catch语句块可以写多个
- 但是从上到下执行catch,必须从小类型异常到大类型异常进行捕捉
- try…catch…中最多执行1个catch语句块。执行结束之后try…catch…就结束了
2.2 示例代码
- read方法:尝试将字符读入指定的字符缓冲区,源码中看到在read构造方法中看到在声明的位置上使用了throws IOException
package 异常机制;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class ExceptionTest03 {
public static void main(String[] args) {
try {
//程序执行到此处发生了FileNotFoundException类型的异常
//JVM会自动创建一个FileNotFoundException类型的对象,将该对象的内存地址赋值给catch语句块中的e变量
FileInputStream fis = new FileInputStream("D:/a.txt");
//上面的代码出现了异常,try语句块的代码不再继续执行,直接进入catch语句块中执行
System.out.println("gggggggggggggggg");
fis.read();
} catch (FileNotFoundException e) { //e内存地址指向堆中的那个对象是“FileNotFoundException类型的”事件
System.out.println("读取的文件不存在");
//FileNotFoundException将Object中的toString方法重写
System.out.println(e); //java.io.FileNotFoundException: D:a.txt (系统找不到指定的文件。)
} catch (IOException e) {
System.out.println("其他IO异常");
}
System.out.println("aaa");
}
}
- 程序执行到读取特定文件的时候,发生了FileNotFoundException类型的异常
- JVM会自动创建一个FileNotFoundException类型的对象,将该对象的内存地址赋值给catch语句块中的e变量
- e内存地址指向堆中的那个对象是“FileNotFoundException类型的”事件
- FileNotFoundException将Object中的toString方法重写
- 控制台输出信息:
读取的文件不存在
java.io.FileNotFoundException: D:a.txt (系统找不到指定的文件。)
aaa
2.3 总结
- try{} 不能单独存在,后面必须跟上 catch 或者 finally , catch可以有多个
三、关于printStackTrace和getMessage方法的应用
- 如何取得异常对象的具体信息,常用的方法主要有两种
- 取得异常的堆栈信息(比较适合于程序调式阶段),printStackTrace()
- 取得异常描述信息:getMessage()
1.printStackTrace方法
package 异常机制;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class ExceptionTest04 {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("D:a.txt");
} catch (FileNotFoundException e) {
//打印异常堆栈信息
//一般情况下都会使用该方式去调式程序
e.printStackTrace();
// String message = e.getMessage();
// System.out.println(message); //D:a.txt (系统找不到指定的文件。)
}
System.out.println("abc");
}
}
- 控制台输出
java.io.FileNotFoundException: D:a.txt (系统找不到指定的文件。)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at 异常机制.ExceptionTest04.main(ExceptionTest04.java:9)
abc
2.getMessage方法
- 将上面13行代码注释掉,15、16行代码打开,运行代码
- 控制台输出
D:a.txt (系统找不到指定的文件。)
abc
3.总结
- 从上述两个打印信息看出,printStackTrace打印异常信息比较详细,getMessage打印的异常信息较为简洁,printStackTrace打印的信息包含了getMessage打印出来的信息,所以一般使用printStackTrace来打印异常信息
四、finally语句块
- finally语句块中可以直接和try语句块联用 try…finally…
- try…catch…finally也可以
- 在finally语句块中的代码是一定会执行的
- 只要在执行finally语句块之前退出了JVM,则finally语句块就不会执行
- 通常在程序中为了保证某资源一定会释放,所以一般在finally语句块中释放资源
package 异常机制;
public class ExceptionTest06 {
public static void main(String[] args) {
int i = m1();
System.out.println(i); //10
}
public static int m1(){
int i = 10;
try{
return i;
}finally {
i++;
System.out.println("m1的i="+i); //m1的i=11
}
}
}
- 输出结果
m1的i=11
10
return i;
分开来可以写成int temp = i; return temp;
,操作的是int i = 10;
- finally语句块中的
i++;
操作的是int i = 10;
package 异常机制;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class ExceptionTest07 {
public static void main(String[] args) {
//必须在外边声明
FileInputStream fis = null;
try {
fis = new FileInputStream("D:a.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
if (fis!=null){
try{
fis.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
}
- 流是个资源,为了保证资源一定会释放,用完我们要关闭