Java异常机制使异常处理代码和正常业务代码分离,让程序有更好的容错性、更加健壮。目前主流编程语言如C++、C#、Ruby、Python等都提供了异常处理机制。
Javc异常主要依赖于try、catch、finally、throw、throws五个关键字,Java7进一步增强了异常处理机制的功能,包括带资源的try语句,捕获多异常的catch两个新功能(简化了异常处理)。
Java将异常分为两种:Checked异常和Runtime异常
Java常见异常思维导图整理如下:
我们先来看看异常处理语法结构:
try { // 业务流程 ... } catch(Exception e) { // 异常处理块 ... } finally { //资源回收块 ... }
1)当执行try的业务逻辑代码出现异常,系统自动生成一个异常对象并提交给Java运行时环境,即抛出异常(throw)。
2)当Java运行时环境收到异常对象时,会寻找处理该异常的catch块(依次判断该异常对象是否是catch块后异常类或者其子类的实例),找到则交付处理,即捕获异常(catch),如果找不到对应的catch块,Java运行时环境终止,程序在此退出。
所有的异常对象都包含如下几个常用方法:
getMessage():返回该异常的详细描述字符串
printStackTrace():将该异常的跟踪栈信息输出到标准输出流
printStackTrace(PrintStream s):将该异常的跟踪栈信息输出到指定输出流
getStackTrace():返回该异常的跟踪栈信息
示例:
try { ... } catch(NullPointerException e) { System.out.println(e.getMessage()); // 返回该异常的详细描述字符串 e.printStackTrace(); // 将该异常的跟踪栈信息输出到标准输出流 }
当前方法不知如何处理出现的异常,抛由上一级调用者处理;如果main方法也要抛出异常,则交由JVM处理(打印异常的跟踪栈信息并中止程序运行)
public class ThrowsDemo{ // 此处异常将交由JVM处理 public static void main(String[] args) throws IOException{ ... } }注意:子类方法抛出的异常范围应小于等于父类
自行抛出异常,throw抛出的是一个异常实例,每次只能抛出一个
throw抛出Checked异常:throw语句要放在try块里或者一个带throws声明抛出的方法中
throw抛出Runtime异常:自由放
try ( if(...){ throw new RuntimeException("我是一个throw抛出的异常.."); } ... } catch(Exception e) { ... } finally { ... }
继承Exception 或者 RuntimeException(Runtime异常),提供两个构造方法:无参和带一个字符串(异常对象的描述信息,getMessage()的返回值)参数的构造方法
public class MyException extends RuntimeException{ // 无参构造方法 public MyException(){} // 带一个参数的构造方法 public MyException(String msg){ super(msg); // } }
多个方法协作处理同一个异常,可在catch块中结合throw语句完成,在大型企业级应用中常见,企业级应用对异常处理通常分为两部分:(1)后台通过日志记录异常详情;(2)应用向应用使用者传达提示。
public class ExceptionTest{ public void init(String msg) throws MyException{ try ( ... } catch(Exception e) { e.printStackTrace(); // 再次抛出异常 throw new MyException("必须是数字.."); ... } if(...){ throw new MyException("数字必须大于5位.."); } } public static void main(String[] args){ ExceptionTest et = new ExceptionTest(); try ( ... } catch(MyException e) { // 再次捕获到异常并处理 System.err.println(e.getMessage()); } } }
分层结构:表现层(用户界面)-->中间层(业务逻辑)-->持久层(保存数据)
上层功能的实现严格依赖于下层的API,不会跨层访问
异常转译:捕获异常后抛出一个新的业务异常(包含对用户的提示信息)
这种把捕获的一个异常接着抛给另一个异常,并把原始异常信息保存下来是一种典型的链式处理(23种设计模式之一:职责链模式),也被称为“异常链”。
当产生异常的时候,异常从方法1触发,传到方法2,再传到方法3...最后传到main方法,在main方法终止,这个过程就是Java的异常跟踪栈。
方法调用栈:面向对象的应用运行时,会发生一系列方法调用,从而形成“方法调用栈”。
1)Java7提供了多异常捕获
try { ... } catch(IndexOutOfBoundsException|NumberFormatException|NullPointerException e) { e.printStackTrace(); }
2)Java7增强了try语句的功能
允许在try关键字后加 ( ) ,圆括号可声明、初始化一个或多个资源(指那些必须在程序结束时显式关闭的资源,比如数据库连接、网络连接、流等,这些资源实现类必须实现AutoCloseable或Closeable,实现close()方法),try语句在该语句结束的时候自动关闭这些资源。
自动关闭资源的try语句相当于包含了隐式的finally块(用于关闭资源),故这个try语句可以既没有catch块,也没有finally块。
try ( // 声明、初始化可关闭资源,try语句会自动关闭 BufferedReader br = new BufferedReader(new FileReader("Test.java"));){ // 使用资源 System.out.println(br.readLine()); ... } catch(Exception e) { ... } finally { ... }
try ( ... } catch(Throwable t) { t.printStackTrace(); }4)不要忽略捕获到的异常
1)try后的 { } 不可以省略
2)父类异常的catch块都应该排在子类异常catch块的后面
3)异常处理结构try块是必需的,catch块和finally块至少出现其中一个
4)return方法
try { ... } catch(Exception e) { return; // 强制方法返回 // System.exit(1); // 退出虚拟机,finally块失去执行的机会 } finally { ... }5)尽量避免finally块中使用return或throw等导致方法终止的语句
try { ... return true; // 失效,这里不会终止方法,接下来还会执行finally块, // 除非调用System.exit(1);退出虚拟机,finally块将失去执行的机会 } catch(Exception e) { ... } finally { ... return false; // 返回false }