在正常的程序设计中,程序异常处理是非常关键和重要的一部分。试想一个项目中没有一个好的异常处理,这个项目会怎么样?
什么是异常
异常其实是程序上的错误,包括程序逻辑错误和系统错误。比如数组下标越界、内存溢出等,这些都是意外的情况,错误在我们的程序的编写过程中会经常发生,包括编译期间和运行期间的错误。在编译期间出现的错误编译器会帮助我们修正,可是在运行期间的错误编译器就无能为力了,并且运行期间的错误往往是难以预料的。
程序出现了错误,我们不能不去处理,这样的程序的健壮性太差了。为了提高程序的健壮性我们要合理的解决这些错误!于是Java中提供了异常的处理机制,通过异常来处理程序运行期间中出现的错误。通过这一特性,我们可以很好的提高程序的健壮性。
Java是一个全面的面向对象语言,不像PHP那样,既支持过程式编程,也支持面向对象编程。Java中异常的父类是java.lang.Throwable类。在Java中定义很多的的异常类,比如OutOfMenoryError、NullPointerException、IndexOutOfBoundsException等。
Exception 类的层次
所有的异常类是从 java.lang.Exception 类继承的子类。
- Exception 类是 Throwable类的子类。除了Exception类外,Throwable还有一个子类Error 。
Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。 - Error 用来指示运行时环境发生的错误。
例如,JVM 内存溢出。一般地,程序不会从错误中恢复。
异常类有两个主要的子类:IOException 类和 RuntimeException 类。
Exception,也就是我们经常见到的一些异常情况,例如NullPointerException、IndexOutOfBoundsException等,这些异常时是我们可以处理的异常。
Exception类的异常包括checked exception和unchecked exception(unchecked exception也称作运行时异常RuntimeException,Exception类的异常都是在运行期间发生的),对于运行时异常,Java编译器不要求必须进行异常处理捕获处理或者抛出,这个由程序员自行决定。
checked exception(检查异常),也称为非运行时异常(运行时异常以外的异常就是非运行时异常),Java编译器强制程序员必须捕获处理,比如常见的IOException和SQLException。对于非运行时异常如果不进行捕获或者抛出处理,Java编译器都不会通过。
在网上找了一个图,能够很清楚的描述在Java中,异常类的结构层次(有些时候语言就略显苍白,不如图片或者视频表现力丰富)。
在Java中,所有的异常都是继承至java.lang.Throwable类。Error类是error类型异常的父类,Exception类是exception类型异常的父类,RuntimeException类是所有运行时异常的父类,RuntimeException以外的并且继承Exception的类是非运行时异常。
- 典型的RuntimeException包括NullPointerException、IndexOutOfBoundsException、IllegalArgumentException等。
- 典型的非RuntimeException包括IOException、SQLException等。
Java如何处理异常
在Java中如果需要处理异常,必须先对异常进行捕获(这一点是和PHP是相同的)。使用try和catch关键字进行处理。具体的规则如下:
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}finally{
// 程序代码
}
来段实际的代码:
import java.io.*;
public class ExcepTest{
public static void main(String args[]){
try {
File file = new File("/Users/sam/a.txt");
if(!file.exists())
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
被try块包围的代码说明这段代码可能会发生异常,一旦发生异常,异常便会被catch捕获到,然后需要在catch块中进行异常处理。这是一种处理异常的方式。在Java中还提供了另一种异常处理方式即抛出异常,顾名思义,也就是说一旦发生异常,我把这个异常抛出去,让调用者去进行处理,自己不进行具体的处理,此时需要用到throw和throws关键字。
我们看下面的代码:
public class Main {
public static void main(String[] args) {
try {
createFile();
} catch (Exception e) {
// TODO: handle exception
}
}
public static void createFile() throws IOException{
File file = new File("/Users/sam/a.txt);
if(!file.exists())
file.createNewFile();
}
}
这段代码和上面一段代码的区别是,在实际的createFile方法中并没有捕获异常,而是用throws关键字声明抛出异常,即告知调用者此方法可能会抛出IOException,需要调用者进行捕获处理。那么在main方法中调用createFile方法的时候,采用try...catch块进行了异常捕获处理。
还可以使用throw关键字进行抛出异常。看下面的例子:
package exception;
/**
* Created by Sam on 18/6/17.
*/
public class Main {
public static void main(String[] args){
People people = new People("Sam",new Byte("25"));
people.sayAge();
}
}
class People {
private String name;
private Byte age;
public People(String name, Byte age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Byte getAge() {
return age;
}
public void setAge(Byte age) {
this.age = age;
}
public Byte sayAge(){
if (age > Byte.MIN_VALUE){
throw new MyException("年龄太大了","100" );
}
return age;
}
}
public class MyException extends RuntimeException {
private String code;
public MyException(String message, String code) {
super(message);
this.code = code;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
上面就是利用throw关键字进行手动抛出异常。调用者可以捕获处理异常,也可以不用处理异常。下面我们就修改一下代码进行捕获处理:
public class Main {
public static void main(String[] args){
People people = new People("Sam",new Byte("25"));
try {
people.sayAge();
}catch (Exception e){
System.out.println(e);
}
}
}
程序输出:
exception.MyException: 年龄太大了
也就说在Java中进行异常处理的话,对于可能会发生异常的代码,可以选择三种方法来进行异常处理:
1、对代码块用try..catch进行异常捕获处理;
2、在该代码的方法体外用throws进行抛出声明,告知此方法的调用者这段代码可能会出现这些异常,你需要谨慎处理。此时有两种情况:
如果声明抛出的异常是非运行时异常,此方法的调用者必须显示地用try..catch块进行捕获或者继续向上层抛出异常。
如果声明抛出的异常是运行时异常,此方法的调用者可以选择地进行异常捕获处理。
3、在代码块用throw手动抛出一个异常对象,此时也有两种情况,跟2)中的类似:
如果抛出的异常对象是非运行时异常,此方法的调用者必须显示地用try..catch块进行捕获或者继续向上层抛出异常。
如果抛出的异常对象是运行时异常,此方法的调用者可以选择地进行异常捕获处理。(如果最终将异常抛给main方法,则相当于交给jvm自动处理,此时jvm会简单地打印异常信息)
关于Java的异常机制就暂时说到这里。