异常
异常所带来的一个相当明显的好处是,它能降低错误代码处理的复杂度。
最大的异常是Throwable,异常可分为可修复异常(Exception)和不可修复异常(Error)
Exception:程序本身及环境
Error:内部系统,堆内存溢出错误,引用不释放,会造成内存泄露问题
对于程序中的问题有三种
- 编译期异常
- 运行时异常
- 逻辑异常
常用的异常
RuntimeException 运行时的异常
NullPointerException 空指针异常
ArithmeticException 算术运算异常
ArrayIndexOutofBoundsException 数组下标越界异常
ClassCastException 类型转换异常
FileNotFoundException 文件找不到异常
IOException IO异常
异常处理程序
try {
// 可能产生异常的代码
} catch (Type1 id1) {
// 异常信息1
} catch (Type2 id2) {
// 异常信息2
} catch (Type3 id3) {
// 异常信息3
} finally {
//总会被执行的代码
}
异常的级别:异常1 < 异常2 < 异常3
异常处理理论上有两种基本模式:
- Java支持终止模式。在这种模式中,一旦异常被抛出,就表明错误已无法挽回,也不能回来继续执行。
- 恢复模式:意思是异常处理程序的工作是修正错误,然后重新尝试调用出问题的方法,并认为第二次成功。(把try放在while循环中,这样就可以不断进入try)
恢复模式:开始显得很吸引人,但是不实用,其中主要的原因可能是它所导致的耦合:恢复性的处理程序需要了解异常抛出的地点,这这势必要包含依赖抛出位置的非通用性代码,增加了代码编写和维护困难。
自定义异常
自己定义异常类,必须从已有的异常类继承
public class SimpleException extends Exception {
public SimpleException(String message) {
super(message);
System.out.println(message);
}
}
public class InheritingException {
public void f() throws SimpleException{
System.out.println("Throw SimpleException from f()");
throw new SimpleException("没错,这就是我自己定义的一个简单的异常");
}
public static void main(String[] args) {
InheritingException in = new InheritingException();
try {
in.f();
}catch (SimpleException s){
System.out.println("这个简单的异常被我抓住了");
}
}
}
重新抛出异常:
把异常抛给上一级环境中的异常的异常处理程序,同一个try块的后续catch子句将被忽略。
异常链:
在捕获一个异常后抛出另外一个异常,并且希望把原始的信息保存下来,这就被称为异常链
public classIOException extends Exception{
//定义异常的原因
publicIOException(String message){
super(message);
}
//定义异常原因,并携带原始的异常
publicIOException(String message,Throwable cause){
super(message,cause);
}
//保留原始异常信息
publicIOExcepiton(Throwable cause){
super(cause);
}
}
finally
对于没有垃圾回收和析构函数自动调用机制的语言来说,finally他能保证,无论try块里面发生什么,内存总能得到释放
finally主要用在:已经打开的文件或者网络连接,在屏幕上画的图形,甚至是外部世界的某个开关
无论finally是否可以和break,continue,return使用,仍然会执行
有以下五种特殊情况下,finally块不会被执行:
1. 在finally语句块中发生了异常
2. 在前面的代码中使用了System.exit(0)退出程序。这个System.exit(0)代码是终止Java虚拟机的运行,即退出结束当前的程序。
3. 程序所在的线程死亡。
4. 关闭CPU。
5. 如果一个方法内在执行try{}语句之前就已经return了。只有与 finally 相对应的 try 语句块得到执行的情况下,finally 语句块才会执行。
Java异常类层次结构图
例题:finally在return之前执行还是在return之后执行?:在return中间执行。
public class Test {
public static void main(String[] args) {
System.out.println(Test.method());
}
public static int method(){
try {
return 1;
} catch (Exception e) {
return 0;
} finally {
return 2;
}
}
}
//执行结果
2
结论:
1.执行过程:这里仅仅需要注意的是在try{}语句中执行到return 1 会在临时栈中存储值为1的变量。接着回去执行finally里面的内容,这时执行finally中的return 2;方法,这时临时栈中的值就是变为 2,会覆盖原来临时栈中的值1.所以它的返回值为2。
2.finally 语句块在 try 语句块中的 return 语句之前执行。3.finally 语句块在 catch 语句块中的 return 语句之前执行。
异常的使用指南
使用异常的情况
- 在恰当的级别处理问题
- 解决问题并重新调用产生异常的方法
- 进行少许修补,然后绕过异常发生的地方继续执行
- 用别的数据进行计算,以代替方法预计会返回的值
- 把当前运行环境下能做的事情尽量做完,然后把相同(不相同)的异常重抛到更高层
- 终止程序
- 进行简化
- 让类库和程序更安全