异常处理
异常是指程序运行时(非编译时)所发生的非正常情况或错误,当程序违反了语义规则时,JVM就会将出现的错误表示为一个异常并抛出。这个异常可以在catch程序块中进行捕获,然后进行处理。
异常处理的目的就是为了提高程序的安全性与健壮性。
1. Error&Exception
1.1 Error
Error表示程序在运行期间出现了非常严重的错误,并且该错误是不可恢复的,由于这属于JVM层次的严重错误,所以这种错误是会导致程序终止执行的。
此外,编译器不会检查Error是否被处理,因此,在程序中不推荐去捕获Error类型的异常,主要原因是运行时异常多是由于逻辑错误导致的,属于应该解决的错误。当异常发生时,JVM一般会选择将线程终止。
1.2 Exception
Exception表示可恢复的异常,是编译器可以捕捉到的。它包含两类:检查异常和运行时异常。
1. 检查异常
检查异常是在程序中最经常遇见的异常,所有继承自Exception并且不是运行时异常的异常都是检查异常,如IO异常或SQL异常等。对于这种异常,都发生在编译阶段,Java编译器强制程序去捕获此类异常。
- 异常的发生并不会导致程序的出错,进行处理后可以继续执行后续的操作;
- 程序依赖于不可靠的外部条件
2. 运行时异常
对于运行时异常,编译器没有强制对其进行捕获并处理。如果不对这种异常进行处理,当出现这种异常时,会由JVM来处理。在Java语言中,最常见的运行时异常有:空指针异常、数据存储异常、类型转换异常、数组越界异常、缓冲区溢出异常、算术异常等。
出现运行时异常后,系统会把异常一直往上层抛出,直到遇到处理代码为止。 如果没有处理快,则抛到最上层;如果是多线程就由Thread.run()方法抛出,如果是单线程,就被Main()方法抛出。
抛出后,如果是其他线程,这个线程也就退出了。如果是主程序抛出的异常,那么整个程序也就退出了。
如果不对运行时异常进行处理,后果是很严重的。 一旦发送,要么线程中止,要么程序终止。
2. Java异常处理机制
2.1 try/catch
使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。try/catch代码块中的代码称为保护代码,使用 try/catch 的语法如下:
try
{
// 程序代码
}catch(ExceptionName e1)
{
//Catch 块
}
Catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。
如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样。
2.2 finally关键字
finally 关键字用来创建在 try 代码块后面执行的代码块。无论是否发生异常,finally 代码块中的代码总会被执行。在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。
finally 代码块出现在 catch 代码块最后,语法如下:
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}finally{
// 程序代码
}
注意下面事项:
- catch 不能独立于 try 存在。
- 在 try/catch 后面添加 finally 块并非强制性要求的。
- try 代码后不能既没 catch 块也没 finally 块。
- try, catch, finally 块之间不能添加任何代码。
2.3 throws/throw 关键字
如果一个方法没有捕获一个检查性异常,那么该方法必须使用 throws 关键字来声明。throws 关键字放在方法签名的尾部。
也可以使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。
下面方法的声明抛出一个 RemoteException 异常:
import java.io.*;
public class className
{
public void deposit(double amount) throws RemoteException
{
// Method implementation
throw new RemoteException();
}
//Remainder of class definition
}
3. 异常流程处理
- finally语句不被执行的唯一情况是先执行了用于终止程序的System.exit()方法
- return语句用于退出本方法
- 建议不要在finally代码块中使用return或throw
- 在运行时环境,并不会区分异常的类型,所以程序员自己要遵从良好的实践原则,否则Java异常处理机制就会被误用。
- finally代码块总是会在方法返回或方法抛出异常前执行,而try-catch-finally代码块后面的代码就有可能不会再执行。
- try代码块一定要求要有一个catch代码块或finally代码块(二者取其一就行)。
- catch处理器的优先级比声明异常语句要高。
- 如果多处抛出异常,finally代码块里面的异常会压抑其他异常。
4. 常见问题
4.1 throw与throws的比较
1、throws出现在方法函数头;而throw出现在函数体。
2、throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常对象。
3、两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。
4.2 final、finally、finalize的区别
-
final修饰符(关键字)。被final修饰的类,就意味着不能再派生出新的子类,不能作为父类而被子类继承。因此一个类不能既被abstract声明,又被final声明。将变量或方法声明为final,可以保证他们在使用的过程中不被修改。被声明为final的变量必须在声明时给出变量的初始值,而在以后的引用中只能读取。被final声明的方法也同样只能使用,不能重载。
-
finally是在异常处理时提供finally块来执行任何清除操作。不管有没有异常被抛出、捕获,finally块都会被执行。try块中的内容是在无异常时执行到结束。catch块中的内容,是在try块内容发生catch所声明的异常时,跳转到catch块中执行。finally块则是无论异常是否发生,都会执行finally块的内容,所以在代码逻辑中有需要无论发生什么都必须执行的代码,就可以放在finally块中。
-
finalize是方法名。java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在object类中定义的,因此所有的类都继承了它。子类覆盖finalize()方法以整理系统资源或者被执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。