异常指不期而至的各种状况,它在程序运行的过程中发生。作为开发者,我们都希望自己写的代码永远都不会出现 bug,然而现实告诉我们并没有这样的情景。如果用户在程序的使用过程中因为一些原因造成他的数据丢失,这个用户就可能不会再使用该程序了。所以,对于程序的错误以及外部环境能够对用户造成的影响,我们应当及时报告并且以适当的方式来处理这个错误。
异常都是从 Throwable 类派生出来的,而 Throwable 类是直接从 Object 类继承而来。
异常通常有四类:
平时主要关注的异常是 Exception 下的异常,而 Exception 异常下又主要分为两大类异常,一个是派生于 RuntimeExcption 的异常,一个是除了 RuntimeExcption体系之外的其他异常。
RuntimeExcption 异常(运行时异常)通常有以下几种:
一般来说,RuntimeException 都是代码逻辑出现问题。
非 RuntimeException(受检异常,Checked Exception)一般有:
throw 抛出异常
当程序运行时数据出现错误或者我们不希望发生的情况出现的话,可以通过抛出异常来处理。
异常抛出语法:
throw new 异常类();
public class ThrowTest {
public static void main(String[] args) {
Integer a=1;
Integer b=null;
if(a==null||b==null){
throw new NullPointerException();
}else{
System.out.println(a+b);
}
}
}
throws 声明异常
throws 用于声明异常,表示该方法可能会抛出的异常。如果声明的异常中包括 checked 异常(受检异常),那么调用者必须捕获处理该异常或者使用 throws 继续向上抛出。throws 位于方法体前,多个异常之间使用 , 分割。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class ThrowTest {
public static void main(String[] args) throws FileNotFoundException {
ThrowTest();
}
private static void ThrowTest() throws FileNotFoundException{
new FileInputStream("home/test.java");
}
}
捕获异常
通常抛出异常后,还需要将异常捕获。使用 try 和 catch 语句块来捕获异常,有时候还会用到 finally。
try 语句块是必不可少的,catch 和 finally 语句块可以根据情况选择其一或者全选。你可以把可能发生错误或出现问题的语句放到 try 语句块中,将异常发生后要执行的语句放到 catch 语句块中,而 finally 语句块里面放置的语句,不管异常是否发生,它们都会被执行。
public static void main(String[] args) {
try {
System.out.println("I am try block!");
Class<?> tempClass=Class.forName("");
System.out.println("Bye! Try block!");
} catch (ClassNotFoundException e) {
// TODO: handle exception
System.out.println("I am catch block!");
e.printStackTrace();
System.out.println("Bye! Catch block");
}finally{
System.out.println("I am finally block.");
}
}
}
捕获多个异常
由于各种原因抛出多种不同的异常,而对于不同的异常,我们希望用不同的方式来处理它们,而不是笼统的使用同一个方式处理,在这种情况下,可以使用异常匹配,当匹配到对应的异常后,后面的异常将不再进行匹配。
在处理异常时,并不要求抛出的异常同 catch 所声明的异常完全匹配,子类的对象也可以匹配父类的处理程序。比如异常 A 继承于异常 B,那么在处理多个异常时,一定要将异常 A 放在异常 B 之前捕获,如果将异常 B 放在异常 A 之前,那么将永远匹配到异常 B,异常 A 将永远不可能执行,并且编译器将会报错。
public class MultipleCapturesDemo {
public static void main(String[] args) {
try {
new FileInputStream("");
} catch (FileNotFoundException e) {
// TODO: handle exception
System.out.println("文件未找到!");
}catch(Exception e){
System.out.println("发生异常!");
}
}
}
自定义异常
自定义一个异常类非常简单,只需要让它继承 Exception 或其子类就行。在自定义异常类的时候,建议同时提供无参构造方法和带字符串参数的构造方法,后者可以为你在调试时提供更加详细的信息。
异常堆栈
当异常抛出后,我们可以通过异常堆栈追踪程序的运行轨迹。
public class ExceptionStackTrace {
private static void method1(){
method2();
}
private static void method2(){
throw new NullPointerException();
}
public static void main(String[] args) {
try {
method1();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
java.lang.NullPointerException
at Project.ExceptionStackTrace.method2(ExceptionStackTrace.java:11)
at Project.ExceptionStackTrace.method1(ExceptionStackTrace.java:7)
at Project.ExceptionStackTrace.main(ExceptionStackTrace.java:15)
上面的异常堆栈轨迹,在对比我们方法的调用过程,可以得出异常信息中首先打印的是距离抛出异常最近的语句,接着是调用该方法的方法,一直到最开始被调用的方法。从下往上看,就可以得出程序运行的轨迹。