第10章:异常处理

10.1 异常概述

将业务功能实现代码与错误处理的代码相分离

10.2 异常处理机制

10.2.1使用try…catch捕获异常
//没有异常处理之前对异常的处理方式
if(一切正常){
    //业务实现代码
}else{
    alert 输入不合法;
    goto retry;
}
//利用try...catch处理异常
try{
    //业务实现代码
    throw new RuntimeException();
}catch(Exception e){
    alert 输入不合法;
    goto retry;
}
  1. 当代码出现异常时,系统会自动生成一个异常对象,该异常对象被提交给java运行时环境,该过程被称为抛出(throw)异常
  2. java运行时环境接收到异常对象后,寻找能处理该异常对象的catch块,如果找到合适的catch块,把该异常对象交给catch块处理,这个过程称为捕获(catch)异常,如果找不到catch块,java运行环境终止,java程序退出
10.2.2 异常的继承体系
  1. java运行时对象接收到异常对象后,从上到下依次判断该异常对象是否为catch块后对应的异常类或其子类的实例,如果是,执行该catch块,如果不是与下一个catch块进行比较
  2. try块被执行一次,则try块后只有一个catch块会被执行,绝不可能有多个catch块被执行
  3. 即使try块和catch块中只有一行代码,{}也不可以被省略,try块内声明变量只在try块中有效,catch块中无法访问该变量
  4. 所有父类异常的catch块应该排在子类异常catch块的后面(先处理小异常,再处理大异常),否则编译错误。因为父类异常的catch块若排在前面,会导致子类异常catch块永远不会被执行
  5. 继承体系
    1. java把所有非正常情况分为异常(Exception)和错误(Error)
    2. Error错误指与虚拟机相关问题,如系统崩溃虚拟机错误等,这种错误无法恢复,通常应用程序无法处理这些错误,所以程序中不应用catch来捕获Error对象,也无需在再定义方式时throws Error及其任何子类,虽然实际上编译器允许这样做,但这样做没有意义
      在这里插入图片描述
10.2.3 java7提供的多异常捕获
//多异常捕获不同异常类型用|分隔
catch(IndexOutOfBoundsException|NumberFormatException ie){
    //编译报错,多异常捕获时,异常对象默认为final修饰
    //ie = new ArithmeticException("test");
}
10.2.4 访问异常信息

Throwable接口包含的方法

getMessage():返回该异常的详细描述字符串
printStackTrace():将该异常的跟踪栈信息输入到标准错误输出
printStackTrace(PrintStream s):将该异常的跟踪栈信息输入到指定输出流
getStackTrace():返回该异常的跟踪栈信息
10.2.5 使用finally回收资源

java垃圾回收机制不会回收任何物理资源(数据库连接,网络连接,磁盘文件等),只能回收堆内存中对象所占用的资源,回收物理资源如果放在try块中,由于异常会导致异常之后的代码不执行,如果放在catch快中,catch块中语句也很有可能不被执行,因此产生finally块来显示回收物理资源

  1. 除非在try块或catch块中调用了退出虚拟机的方法(System.exit()),否则finally块一定执行
  2. 异常处理语法结构中,try块必须存在,catch和finally块有且必有其中之一,必须先try再catch再finally
  3. 如果try或catch块中使用了return或throw语句,系统会在该方法中查找是否有finally块存在,如果有,先执行finally块中代码,最后回来执行该return或throw语句,但如果在finally块中也有return或throw语句,那么在执行到finally该行代码时,会立即跳出该方法,导致try或catch块中的return或throw不再执行,所以应尽量避免在finally块中使用return或throw
10.2.6 异常处理的嵌套

在try或catch或finally中包含完整的异常处理流程的情形称为异常处理的嵌套,一般不超过两层

10.2.7 自动关闭资源的try语句

使用finally块关闭资源时,程序会显得很臃肿,java7之后允许try后跟一对(),()内声明,初始化一个或多个资源,此处资源指那些必须在程序结束后显式关闭的资源,即这些资源类必须实现AutoCloseable或Closeable接口,即实现了close()方法,java7中几乎所有资源类都实现了这两个接口

//这种自动关闭资源的try块相当于包含了隐式的finally块,所以可以既没有catch块又没有finally块
try (BufferedReader by = new BufferedReader(new FileReader("Auto.java"));
				BufferedReader b2 = new BufferedReader(new FileReader("Auto.java"))) {

}

10.3 Runtime异常与Checked(非Runtime)异常

首先Error没有捕获的意义,其次在Exception可以分为Runtime异常和Checked异常(非Runtime),java认为Checked异常都是可以被修复的异常,所以java程序必须显式处理Checked异常,即必须用try…catch捕获该异常或定义该方法时声明抛出(throws)该异常

10.3.1 使用throws声明抛出异常
  1. java运行时环境收到异常对象后,如果在当前方法中不知道如何处理这种类型的异常,可以使用throws声明抛出异常,将该异常交给上一级调用者进行处理,jvm对异常的处理方法为:打印异常的跟踪栈信息,终止程序,这就是程序遇到异常后自动结束的原因
  2. 处理同一个异常对象时throws和try…catch应任选其一
  3. Checked异常增加了编程的复杂度,同时在重写时限制了子类重写父类的方法抛出的异常,因此大部分时侯都应该使用RuntimeException,而不使用Checked异常,由程序自动抛出异常

10.4 使用throw抛出异常

10.4.1 抛出异常
//由于业务需求不符而产生的异常(非系统问题),需要程序员手动抛出
//throw 异常对象
throw new FatalException("你逻辑有问题");
10.4.2 自定义异常类
  1. 必须继承Exception类,如果想自定义RuntimeException,必须继承RuntimeException类
  2. 通常定义异常类时,应提供两个构造器,一个无参,一个带字符串参数,用于作为该异常对象的getMessage()方法的返回值
package com.wsh.object;

public class AuctionException extends RuntimeException{
	public AuctionException(){
		
	}
	public AuctionException(String message){
		super(message);
	}
}	

10.4.3 catch与throw同时使用

如果异常出现的当前方法中程序只对该异常进行了部分处理,还有些处理需要在该方法的调用者中完成,则应该再次抛出该异常,让该方法调用者也能捕获异常,即catch块中throw异常

package com.wsh.object;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;

public class FileNotFound {
	public static void main(String[] args) throws Exception {
		try{
			new FileOutputStream("a.txt");
		}catch(Exception e){
			throw e;
		}
	}
}

10.4.4 java7增强的throw语句

对于上例,java7以前,默认throw e的e为Exception对象,所以在方法签名中必须为throws Exception,但在java7之后,java编译器会检查throw语句抛出的异常类型只能是FileNotFoundException,所以在方法签名中throws FileNotFoundException也可以编译成功

10.4.5 异常链
  1. 对于企业级应用,经常会有严格的分层关系
    在这里插入图片描述
  2. 如果中间层访问持久层时出现SQLException,程序不应该把底层SQLException异常传到用户界面,原因有两点
    1. SQLException对用户使用系统毫无帮助
    2. 对恶意用户而言,将SQLException暴露出来并不安全
  3. 通常做法为:程序先捕获原始异常并保存,再抛出一个新的业务异常,新的业务异常中包含对用户提示信息,这种处理方式称为异常转译,是一种典型的链式处理,也被称为异常链"
  4. jdk1.4以前程序员必须自己编写代码保存原始异常信息,1.4以后所有Throwable的子类在构造器中都可以接收一个原始异常对象作为参数,即构造器中允许传入异常对象,所以自定义异常时,可以再添加一个构造器,用于传入原始异常对象,从而实现对异常的链式处理
public AuctionException(Thowable t){
	super(t);
}

10.5 java的异常跟踪栈

与调用方法相反,调用方法时由外到内,层层调用,异常传播时,从发生异常处开始,由内向外传递

10.6 异常处理规则

10.6.1 不要过度使用异常:会使运行效率变差
  1. 把异常和普通错误混淆,不再编写任何错误处理代码,而是以简单地抛出异常来代替所有错误处理
  2. 使用异常处理代替流程控制
10.6.2 不要使用过于庞大的try块

虽然编程方便,但分析异常原因的难度增加,应将可能出现异常的程序段分别放在不同的try块中,从而分别捕获异常

10.6.3 避免使用catch all语句

catch all是指一种异常捕获模块,它可以处理程序发生的所有可能异常,无法对不同异常分别处理

catch(Throwable e){
			
}
10.6.4 不要忽略捕获到的异常

一般对异常的处理措施

  1. 对异常进行合适的修复,然后绕过异常发生的地方继续执行,或用别的数据进行计算,以代替期望的方法的返回值,或提示用户重新操作,总之Checked异常应尽量修复
  2. 对异常进行转译
  3. throws声明抛出该异常,让上级调用者处理该异常
发布了32 篇原创文章 · 获赞 0 · 访问量 946

猜你喜欢

转载自blog.csdn.net/hanzong110/article/details/102516382