所谓的异常,就是程序按照正常执行逻辑走着走着遇到问题,崴了脚或闪了腰,已经没法再继续走下去了。怎么办呢?就像有病要治病同样,有异常就得处理异常。Java提供了基本的语法来处理异常:一中是throw(s)语法,叫抛异常;一种是try-catch-finally语法,叫作捕获异常。Java是面向对象的,在Java的世界里一切皆是对象,因此异常天然也被当作是一种对象,当异常发生,就是建立了一个异常对象,咱们使用两种语法来处理异常对象就好了,语法也很简单,下面只简单示例:程序员
public class Test { //继续外抛异常 public void test1() throws FileNotFoundException { new FileInputStream("D://test.txt"); } //本身捕获异常 public void test2() { try { new FileInputStream("D://test.txt"); } catch (FileNotFoundException e) { e.printStackTrace(); }finally{ //无论异常发没发生都总会执行的部分,一般用来处理收尾工做,入内存清理,关闭流等等 } } //本身主动建立异常对象并抛出 public void test3() throws Exception { throw new Exception("抛出异常"); } }
这里很基础,仍是说三点:ide
① 最好从英文词性上区分throw和throws两个关键字。前者是动词,表明抛出,很明显是用在方法内部,执行异常对象的抛出动做;后者是名词,用在方法后面,用来声明方法执行可能抛出的一个或多个异常,多个异常之间用逗号隔开;测试
② finally块不是必须的,除非业务中有必需要执行的部分;若是有finally块,那该部分语句不管方法是否发生异常甚至中途执行return终止方法,该部分都会获得执行;finally块中不能使用return,会形成异常丢失;this
③ 若是你对异常是该抛出和本身处理举棋不定,那布衣博主告诉你一个原则:该背锅背锅,不能背就甩锅!是的,异常和平常中的甩锅行为相似。当别人向你甩来的锅若是你能背,那就接住大胆背锅处理异常,否则就甩锅吧给上层调用者处理。编码
打开JDK API 文档,找到 lang包中 Throwable类,这就是Java异常的祖宗类了。你会发现异常家族体系至关庞大,但大而不乱,由于这祖宗也就两个直接子类 Error 和 Exception。Error 一般指系统错误,程序员无需关心;Exception才是须要抛出或者捕获的异常的基类,其下有不少子类孙类,支系繁茂。对于Exception类型异常,又分了编译期异常和运行时异常两大类,前者是在编码阶段必须处理(捕获或者抛出)的异常类型,不处理程序没法正常编译;后者不用主动处理,一般是程序逻辑上的异常,在程序运行时由虚拟机抛出。只需记住以下谱系图便可:spa
Java标准类库中的异常虽然已经不少了,可是那是标准库中通用的异常状况,不一样项目会有不一样的业务逻辑,这就须要咱们根据本身的业务逻辑来定义、处理本身的异常。最好的自定义方式是从跟本身业务处理意思最接近的异常类继承,好比定义运行时异常能够继承RuntimeException,定义IO异常能够继承IOException。不过对异常情形限定得太窄其实也没有必要了,直接继承异常基类Exception便可:code
public class MyException extends Exception { public MyException() { super(); } public MyException(String message) { super(message); } public MyException(String message, Throwable cause) { super(message, cause); } }
这样简单的继承,只是屏蔽了底层的具体异常类,对外暴露的是和本身业务相关的语义化的异常类名而已,功能依旧是父类异常的功能。这样的自定义异常显然功能太父类化了。更多的状况下,咱们但愿异常类能有比父类更完备的功能,以帮助程序向客户端调者反馈更友好的信息。这个时候要跳出异常的字面意思去理解Java的异常机制,不能简单的认为异常就是发生了不想看到的错误,而是把异常当作一种控制流,是程序流程处理的一部分,控制着个人程序在状况1 时走哪一步,在状况2下又该执行哪一步。好比经过JNA调用底层动态库获取硬件信息,因为程序运行的不可预见性,Java程序员和底层语言开发者之间会根据返回值约定一些异常情形,好比 0 表明什么,1 表明什么,诸如此类:对象
public void func(int result) throws MyException1 ,MyException2,MyException3{ switch (result){ case 1:throw new MyException1(); case 2:throw new MyException2(); case 3:throw new MyException3(); ... } }
像上面这种,利用穷举的方式写代码确定是很不明智的,更多的时候,咱们会采用枚举与自定义异常相结合的方式,来处理异常的流程控制,以更好的发挥自定义异常的自定义做用。blog
先定义约定的异常情形枚举:继承
//异常状态枚举 public enum ErrorCode { SUCCESS(0, "成功"), NOTFOUND(1, "未找到验证设备"), READFAIL(2, "读取验证设备失败"), OVERDUE(3, "验证设备已过时"), UNKNOWN(4, "未知缘由的失败"); int code; String msg; ErrorCode(int code, String msg) { this.code = code; this.msg = msg; } public int getCode() { return code; } public String getMsg() { return msg; } //根据状态码获取状态信息 public static ErrorCode getErrorCode(int code) { for (ErrorCode errorCode : values()) { if (errorCode.code == code) { return errorCode; } } return null; } @Override public String toString() { return "ErrorCode{" + "code=" + code + ", msg='" + msg + '\'' + '}'; } }
再定义本身的异常类:
public class LibException extends Exception { private int code; private ErrorCode errorCode; public ErrorCode getErrorCode() { return errorCode; } LibException(int code) { this.code = code; this.errorCode = ErrorCode.getErrorCode(code); } public int getCode() { return code; } public LibException(String message) { super(message); } public LibException(String message, Throwable cause) { super(message, cause); } }
经过枚举和自定义异常的结合,便能在程序中动态的根据错误码反馈给上层调用者友好的异常信息。测试:
public class Test { public static void chenbenbuyi(int result) throws LibException { if(result>=0&&result<=4)throw new LibException(result); System.out.println("result: "+result); } public static void main(String[] args) { try { chenbenbuyi(3); } catch (LibException e) { System.err.println("错误码:"+e.getCode()+" 错误信息:"+e.getErrorCode().getMsg()); } } }
友好输出反馈: