异常简介
异常指阻止当前方法继续执行的问题,如:文件找不到、网络连接失败、非法参数等。发现异常的理想时期是编译阶段,然而编译期间.不能找出所有的异常,余下的问题须在运行期间.
java中的异常分为可查异常和不可查异常
- 可查异常
即编译时异常,指编译器在编译时可以发现的错误,程序在运行时很容易出现的异常状况,这些异常可以预计,所以在编译阶段就必须手动进行捕捉处理,即要么用try-catch语句捕获它,要么用throws子句声明抛出,否则编译无法通过。如IOException、SQLException以及用户自定义的Exception异常 - 不可查异常
不可查异常包括运行时异常(runtimeException)和错误(error),他们都是在程序运行时出现的。异常和错误的区别:异常能被程序本身可以处理,错误是无法处理
运行时异常(runtimeException)指的是程序在运行时才会出现的错误,由程序员自己分析代码决定是否用try...catch进行捕捉处理。如nullpointerException,classcastException,indexoutofboundsException。
错误(error),是程序无法处理的错误,表示运行应用程序中较严重问题,如系统崩溃,虚拟机错误,动态连接失败等。大多数错误与代码编写者执行的操作无关,而表示代码运行时JVM(Java虚拟机)出现的问题。例如,Java虚拟机运行错误(VirtualMachineError),当JVM不再有继续执行操作所需的内存资源时,将出现OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时,如Java虚拟机运行错误(VirtualMachineError)、类定义错(NoClassDefFoundError)等。这些错误是不可查的,即不需要捕获和处理,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。
Java异常类层次结构图:
从上图可以看出Java通过API中Throwable类的众多子类描述各种不同的异常。因而,Java异常都是对象,是Throwable子类的实例.
在Java中,所有的异常都有一个共同的祖先Throwable(可抛出)。Throwable指定代码中可用异常传播机制通过Java应用程序传输的任何问题的共性。
异常处理机制
当异常发生时,将使用new在堆上创建一个异常对象,对于这个异常对象,有两种处理方式.
1.使用throw关键字将异常对象抛出,则当前执行路径被终止,异常处理机制将在其他地方寻找catch块对异常进行处理.
2.使用try...catch在当前逻辑中就进行捕获处理.
throws 和throw
- throws: 一个方法在声明时可以使用throws关键字声明可能会产生的若干异常。
- throw: 抛出异常,并退出当前方法或作用域。
用throws声明要抛出的异常,实际可以不抛出。而用throw抛出的异常也可以不声明。不过Java鼓励程序员把可能会抛出的异常提前声明,这是一种优雅的做法。
自定义异常
Java的异常体系不可能包括所有的异常情况,所以可以自己定义异常类来表示程序中可能会遇到的特定问题。
public class MyException extends RuntimeException {
public MyException() {
}
public MyException(String message) {
super(message);
}
}
在 MyException 这个自定义异常中,定义了两个构造器。一个是默认构造器,一个接收一个字符串作为参数。在第二个构造器中,可以看出调用了基类构造器,所传字符串可以通过getMessage()方法获取。
public class ExceptionTest {
public static void f(){
System.out.println("Throwing Exception from f()");
throw new MyException();
}
public static void g(){
System.out.println("Throwing Exception from g()");
throw new MyException("Originated in g()");
}
public static void main(String[] argv) throws Exception {
try{
f();
}catch(MyException e){
System.out.println(e.getMessage());
e.printStackTrace(System.out);
}
}
}
在main函数中调用g(),运行结果如下
可以总结出以下两个函数:
- getMessage():获取一些描述性信息
- printStackTrace():从方法调用出到异常抛出处的方法调用序列
重新抛出异常
有时希望把捕获的异常重新抛出,在catch中已经得到了对当前异常对象的引用,可以将其重新抛出。
public class ExceptionTest {
public static void g() {
throw new MyException("Originated in g()");
}
public static void h() {
try {
g();
} catch (MyException e) {
System.out.println("An Exception was thrown from g()");
throw e;
}
}
public static void k() throws Exception {
try {
g();
} catch (MyException e) {
System.out.println("An Exception was thrown from g()");
throw (Exception) e.fillInStackTrace();
}
}
public static void main(String[] argv) throws Exception {
try {
h();
} catch (MyException e) {
System.out.println(e.getMessage());
e.printStackTrace(System.out);
}
System.out.println();
try {
k();
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace(System.out);
}
}
}
从运行结果可以看出,如果只是将异常抛出,那么printStackTrace()显示的仍是原来抛出点的调用栈信息,并非重新抛出点的信息,若想更新信息,可以调用
fillInStackTrace()方法,它通过把当前调用栈信息填入原来那个异常对象而建立。这样调用fillInStackTrace()的那一行就会新的异常发生点。
使用finally清理
在java中finally的存在并不是为了释放内存资源,因为java有垃圾回收机制,因此需要java释放的资源主要是:已经打开的文件或网络连接等。
在try中无论有没有捕获异常,finally都会被执行。