异常处理
一、异常
-
异常指程序运行过程中出现的非正常现象,例如用户输入错误,除数为零,需要处理的文件不存在,数组下标越界等。
-
在Java异常处理机制中,引进了很多用来描述和处理异常的类,称为异常类,异常类定义中包含了该类异常的信息和对异常进行处理的方法
二、异常处理
-
异常处理就是程序在出现问题时依然可以正确的执行完
-
Java 采用面向对象的方式来处理异常:
-
抛出异常:在执行一个方法时,如果发生异常,则这个方法生成代表该异常的一个对象,停止当前执行路径,并把异常对象提交给JRE。
-
捕获异常:JRE得到该异常后,寻找相应的代码来处理该异常,JRE在方法的调用栈中查找,从生成异常的方法回溯,直到找到相应的异常处理代码为止。
三、异常分类
-
Exception是程序本身能够处理的异常,比如NullPointerException(空指针异常)、ArrayIndexOutOfBoundsExceptio(数组下标越界异常)、ArithmeticException(算术异常)等
Exception类是所有异常的父类,其子类对应了各种各样可能出现的异常事件 -
Error是程序无法处理的错误,表示运行应用程序中较严重的问题,大多数错误与编写代码执行者的操作无关,表示代码运行时JVM出现的问题,比如JVM运行错误(VirtualMachineError)、当JVM不再有继续执行操作所需的内存资源时,将出现OUtOfMemoryError,这些异常发生时,JVM一般会选择线程终止。综上,Error表明JVM已经处于不可恢复的崩溃状态。
四、运行时异常(RuntimeException)
派生于RuntimeException的异常,产生比较频繁,如果显式的声明或捕获将会对程序的可读性和运行效率有很大影响,因此系统自动检测并将它们交给缺省的异常处理程序
这类异常通常是由编程错误导致的,所以在编写程序时并不要求使用异常处理机制来处理这类异常,只需要添加逻辑处理来避免这些异常
- 算术异常
public class TestDemo1 {
public static void main(String[] args) {
int a = 1;
int b = 0;
System.out.println(a/b);
}
}
- 运行结果:
- 修改代码:
public class TestDemo1 {
public static void main(String[] args) {
int a = 1;
int b = 0;
if (b != 0) {
System.out.println(a/b);
}
}
}
- 空指针异常
public class TestDemo1 {
public static void main(String[] args) {
String str = null;
str.length();
//System.out.println(str)
}
}
- 运行结果:
- 修改代码:
public class TestDemo1 {
public static void main(String[] args) {
String str = null;
if (str != null) {
System.out.println(str.length());
}
}
}
- 数组越界异常
public class TestDemo1 {
public static void main(String[] args) {
int[] array = new int[5];
System.out.println(array[5]);
}
}
- 运行结果:
- 修改代码:
public class TestDemo1 {
public static void main(String[] args) {
int[] array = new int[5];
int a = 5;
if (a < array.length) {
System.out.println(array[a]);
}
}
}
四、已检查异常(CheckedException)
所有不是RuntimeException的异常统称为CheckedException,如IOException等用户自己定义的Exception异常,这类异常在编译时就必须做出处理,否则无法通过编译。
异常处理的方式有两种:
- 使用
try/catch
捕获异常 - 使用
throws
声明异常
- 捕获异常
是通过3个关键词实现:try-catch-finally
用try来执行一段程序,如果出现异常,系统抛出一个异常,可以通过它的类型来捕捉并处理它(catch),最后一步4是通过finally语句为异常处理提供一个统一的出口,finally所指定的代码都要被执行。
-
try:
try语句指定了一段代码,该段代码就是异常捕获并处理的范围,在执行过程中,当任意一条语句产生异常时,就会跳过该条语句中后面的代码,代码中可能会产生并抛出一种或几种类型的异常对象,它后面的catch语句要分别对这些异常做相应的处理
当异常处理的代码执行结束后,不会回到try语句中执行尚未执行的代码,且一个try块必须有至少一个catch块或者一个finally块。 -
catch:
一个try语句块可以伴随一个或者多个catch语句块,用于产生不同类型的异常对象 -
finally:
有些语句不管是否发生了异常都必须要执行,就将这些语句放到finally块中。
try-catch-finally
语句的执行过程:
首先执行可能发生异常的try语句块,如果try语句没有出现异常则执行完后跳转至finally语句块执行,若try语句出现异常则中断执行并根据发生的异常类型跳至相应的catch语句块执行处理,catch语句块可以有多个,分别捕获不同类型的异常,catch语句块执行完后程序会执行finally语句块,finally是可选的,如果有的话,不管是否发生异常,finally块都会被执行。
即使try和catch块中存在return语句,finally语句也会执行,是在执行完finally语句后再通过return退出,
finally语句块只有一种情况下不会执行,在执行finally之前遇到了System.exit(0)结束程序运行。
例:
在D盘下创建一个a.txt,写入Daria.
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class TestDemo1 {
public static void main(String[] args) {
FileReader reader = null;
try {
reader = new FileReader("d:/a.txt");
char ch = (char)reader.read();
System.out.println(ch);
char ch1 = (char)reader.read();
System.out.print(ch1);
char ch2 = (char)reader.read();
System.out.print(ch2);
char ch3 = (char)reader.read();
System.out.print(ch3);
char ch4 = (char)reader.read();
System.out.print(ch4);
} catch (FileNotFoundException e) { //子类异常在父类前面
e.printStackTrace(); //打印异常信息
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 声明异常
- 当CheckedException产生时,不一定要立刻处理它,可以再把异常抛出去;
- 在方法中使用try-catch是由这个方法来处理异常,但是在一些情况下,当前方法并不需要处理发生的异常,而是向上传递给调用它的方法处理;
- 如果一个方法中可能产生某种异常,但是并不能确定如何处理这种异常,应根据异常规范在方法首部声明该方法可能抛出的异常;
- 如果一个方法抛出多个已检查异常,就必须在方法的首部列举出所有异常,中间用逗号隔开。
例:
将上一段代码的捕获异常改为声明异常。
import java.io.FileReader;
import java.io.IOException;
public class TestDemo1 {
public static void main(String[] args) throws IOException { //main方法将异常抛出给JRE
readMyFile();
}
public static void readMyFile() throws IOException{
FileReader reader = null;
reader = new FileReader("d:/a.txt");
System.out.println("=====================");
char ch = (char)reader.read();
System.out.print(ch);
if (reader != null) {
reader.close();
}
}
}
五、自定义异常
- 在程序中,如果jdk提供的任何标准异常类都无法充分描述我们想要表达的问题,这种情况下可以创建自己的异常类,即自定义异常类;
- 自定义异常类只需从Exception类或者它的子类派生一个子类即可;
- 自定义异常类如果继承Exception类,则为受检查异常,必须对其进行处理,如果不想处理可以让自定义异常类继承运行时异常RuntimeException类;
- 习惯上自定义异常应该包含两个构造器:一个是默认构造方法,另一个是带有详细信息的构造方法。
注意:
- 要避免使用异常处理代替错误处理;
- 只在异常情况下使用异常机制;
- 应将整个任务包装在一个try块中;