ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
### 一、什么是异常 异常是程序中一种错误,可能是读取文件错误,网络连接错误,也可能是数组越界错误,或试图使用一个没有被赋值的对象而引起的错误。如果由于异常或错误使程序操作没有操作完成,则程序返回一个安全状态或捕获异常的处理结果,保证程序的健壮性。 #### (1).异常分类 java语言中,定义了许多的异常类,所有异常类都派生Throwable类,而Throwable类又有两个子类Error和Exception,分别表示错误和异常, 其中Exception又分为运行时异常和非运行时异常。下面为异常的体系结构图: ![这里写图片描述](https://box.kancloud.cn/2016-04-13_570df38a866c2.jpg "") #### (2).下面将详细说明图中相关异常的区别和联系 **1.Error和Exception** Error是程序无法处理的错误,通常有内存溢出异常(outofMemoryError),线程终止等,出现这类异常java虚拟机会选择终止程序。 Exception是程序可处理的异常,它又有两个分支运行时异常和非运行时异常,出现这类异常是程序需要处理。 **2.运行时异常和非运行时异常** 运行时异常都是RuntimeException类及其子类异常,如NullPointerException、 IndexOutOfBoundsException等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引 起的,程序应该从逻辑角度尽可能避免这类异常的发生。 非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常, 如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常。 #### (3).什么时候抛出异常 如果遇到无法处理的问题时,该方法可以选择抛出异常,至于何时需要在方法中用throws声明异常,若该方法需要抛出多个异常,则每个异常间用逗号分隔,通常有以下情况需要抛出异常: 1.调用一个已抛出异常的方法,如FileInputStream构造器。 2.程序运行过程错误,并用throw抛出一个检查出的异常。 3.程序出现错误。 4.java虚拟机和运行时库出现的内部异常。 总之一个方法必须声明所有可能抛出的异常已检查异常,所有未检查出的异常要么不可控制,要么就应该避免发生。 #### (4).创建异常 创建异常通常只需要定义派生于Exception类或派生于Exception子类的类。例如定义一个派生于IOException的类,通常定义两个构造器,一个是默认构造器,一个是带有详细描述信息的构造器。如: ~~~ public class MyExcetion extends IOException { private static final long serialVersionUID = 1L; public MyExcetion() { } public MyExcetion(String message) { super(message); } public MyExcetion(Throwable cause) { super(cause); } public MyExcetion(String message, Throwable cause) { super(message, cause); } } ~~~ ### 二、异常处理 程序运行过程中,若出现异常没有及时处理,则会终止程序的执行。可以用捕获异常或抛出异常来处理。 若try语句代码块出现异常,程序会在catch中寻找匹配的异常,然后调用匹配的异常处理器进行处理。运行过程中程序未找到匹配的异常处理器,程序就会终止。 **(1).try…catch模式** 该模式的定义形式如下 try{ 代码块; }catch(Exception e){ } Catch(Exception e2){ } 匹配的原则是:如果抛出的异常对象属于catch子句的异常类,或者属于该异常类的子类,则认为生成的异常对象与catch块捕获的异常类型相匹。 **(2).try…catch…finally模式** 该模包含finally,无论程序有无异常发生,且不管try –catch间是否顺利执行,都会执行finally语句。 **(3).实例说明:** 1.各模块的作用 try块:用于捕获异常,后面可以跟一个或多个catch,finally可有可无,但必须有一个catch块。 catch块:用于处理异常。 finally块是程序是否有无异常,都需要执行的部分。 但在以下3中特殊情况下不会执行: A.在finally 语句中发生异常。 B.程序所在线程死亡。 C.在前面的代码中出现了system.exit()退出程序。 2.当捕获到一个异常对象之后,你可以调用其getMessage()方法来获取异常消息,或者调用printStackTrace()方法来打印执行栈的内容 关于getMessage()方法 当你通过new Exception(“message”)或new RuntimeException(“message”)来创建一个异常对象的时候,传给构造方法的这个参数就是消息,所以,当你捕获到异常对象之后,可以通过getMessage()方法把消息拿出来! 关于printStackTrace()方法它能把执行栈的内容打印出来 ### 三、异常的转换 所谓异常转换,即截获一个异常(往往是checked exception)之后,将其转换为另外一个异常(往往是unchecked exception)抛出。 比如很多情况下,当我们操作数据库的时候或操作文件的时候,JDK类库中的相关操作方法会给我们抛出SQLException或IOException,这些都是checked exception,这些异常一般情况下我们是无法进行处理的,所以,我们需要继续向上抛出异常,假如我们继续抛出SQLException或IOException的话,会导致更上层的程序处理起来非常困难(而且也需要在方法中进行声明)。 因此,大多数情况下,我们在截获到SQLException或IOException之后,会将它转换为一种RuntimeException重新抛出,这是非常常见的异常应用技巧! ### 四、异常链 所谓异常链,即当我们在截获一个异常,在把它转换为另外一个异常的时候,记得把原来那个异常对象设置到新的异常对象中即可。我们不妨观察Throwable类(所有异常类的基类)的构造方法: public Throwable() public Throwable(String message) public Throwable(String message, Throwable cause) public Throwable(Throwable cause) 我们注意到其中的Throwable类型的参数,它就是用来创建异常链的。 下面使用一个实例来说明: **1.自定义异常类:** ~~~ public class MyException extends RuntimeException { private static final long serialVersionUID = 1L; private String errorCode; public MyException() { } public MyException(String message,String errorCode) { super(message); this.errorCode=errorCode; } //用于创建异常链 public MyException(String message,String errorCode,Throwable cause) { super(message, cause); this.errorCode=errorCode; } public String getErrorCode() { return errorCode; } } ~~~ **2.调用该类** ~~~ public class FileUtils { public static String readFile(String filePath){ StringBuffer sb=new StringBuffer(); try { BufferedReader reader=new BufferedReader(new FileReader(filePath)); String line=null; while((line=reader.readLine())!=null){ sb.append(line); } } catch (FileNotFoundException e) { //将原始对象放到新对象中去 throw new MyException("文件没有找到!", "1", e); } catch (IOException e) { throw new MyException("文件读取有误!", "2", e); } return sb.toString(); } } ~~~ ### 五、注意事项 1) 必须在 try 之后添加 catch 或 finally 块。try 块后可同时接 catch 和 finally 块,但至少有一个块。 2) 必须遵循块顺序:若代码同时使用 catch 和 finally 块,则必须将 catch 块放在 try 块之后。 3) 一个 try 块可能有多个 catch 块。若如此,则执行第一个匹配块 即Java虚拟机会把实际抛出的异常对象依次和各个catch代码块声明的异常类型匹配,如果异常对象为某个异常类型或其子类的实例,就执行这个catch代码块,不会再执行其他的 catch代码块。 4) 可嵌套 try-catch-finally 结构。 5) 在 try-catch-finally 结构中,可重新抛出异常。 6)多个catch的顺序一定要遵循子类在上父类在下的规则。 7)一个方法被重写,被重写的方法必须抛出相同的异常或异常的子类。 8)若父类方法抛出多个异常,那子类重写该方法必须抛出哪些异常的一个子集。