首先,思考这样一个问题,为何要使用异常?我对这个问题思考了不少遍,也看到一些答案,可是始终不理解,下面会经过一些实例来探索这个问题的答案。java
异常,表示意外状况。异常是在方法中抛出的,具体来讲,异常是在方法的前置条件不知足的状况下抛出的。例如,有一个方法public static Connection getConnection(String url) throws SQLException能够获取一个数据库链接,须要一个url参数,当方法(API)的调用者在调用这个方法的时候传递进来的url参数为空或者不合法,那么就违反了这个方法的前置条件。mysql
如今来考虑若是没有异常程序的流程怎样执行。无论这个方法是这样实现的,由于不知足依赖,方法确定不能完成它的功能。若是没有异常,那么API的调用者应该怎样处理呢?算法
Connection con = getConnection(url); if(con != null) {正常流程} else{错误处理}
Java异常设计者认为这样的处理方式让业务和错误处理的代码耦合在一块儿了,这不是一个好的方式。另外并非全部的API调用者都会恰当的处理,好比不执行判断,直接使用可能形成程序直接退出,这样是彻底没有恢复机会的,而且没有办法获得错误信息。sql
如今再一次来看使用异常的缘由:数据库
1. 让业务处理代码和错误处理代码解耦工具
2. 提供给API调用者相关的错误信息url
3. 给API调用者相关的恢复机会spa
第一个缘由就不说了,我的有我的的见解。设计
第二个缘由,我的认为这是Java异常最棒的地方,Java异常提供的堆栈信息很是方便帮助定位错误,有利于程序的调试。多是由于习惯了Java异常给咱们的堆栈信息,咱们才认为是理所固然的事情,试着使用一下那些异常定义使用很差的第三方jar包就知道调试没有异常的程序是一种怎样的感觉了。调试
在说第三个缘由以前,先说一个重要的概念,受检异常与非受检异常,非受检异常是只直接或间接继承自RuntimeException类的异常类。其余的异常属于受检异常。
图1 受检异常与非受检异常
受检异常能够理解为是要受到编译器检查的异常,检查什么呢?检查异常有没有被处理,意思就是受检异常必须被捕获或者申明抛出。受检异常是但愿API调用者从异常中恢复。
import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class WhyUseException { private static final String DEFAULT_ALGORITHM = "MD5"; public static void main(String[] args) { @SuppressWarnings("unused") MessageDigest md = null; md = getMessageDigest("sha-256"); } public static MessageDigest getMessageDigest(String algorithm) { MessageDigest md = null; try { return MessageDigest.getInstance(algorithm); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); try { md = MessageDigest.getInstance(DEFAULT_ALGORITHM); } catch (NoSuchAlgorithmException e1) { // e1.printStackTrace(); // 使用默认的md5,肯定是存在的,因此断言不会执行到这个地方 } } return md; } }
如上面程序所示的NoSuchAlgorithmException就是一个受检异常,由于NoSuchAlgorithmException继承自GeneralSecurityException,而GeneralSecurityException继承自Exception,不是直接或者间接的继承自RuntimeException,因此NoSuchAlgorithmException是一个受检异常。
对于受检异常是必需要什么抛出,或者捕获的,这是受检异常设置的意图,但愿从异常中恢复。就像上面程序所示的同样当抛出NoSuchAlgorithmException异常时,咱们的恢复方法就是使用默认的md5算法。可是大多数时候咱们只是但愿获得错误信息,而不是从错误中恢复。这样形成的麻烦是很是多的,由于受检异常必须捕获或者什么抛出,因此就会有不少try catch或者throws申明这样的不是很优雅的代码。这对于API的调用者也绝对是一种负担。想Matin Flower和Think in Java的做者都认为受检异常的麻烦比好处多。
我的认为Java的受检异常的确不够优雅,这主要是最开始写数据链接的代码的时候一直强迫我捕获异常,而且那时候我并不知道为何,也不知道怎样优雅的处理。不过受检异常存在的确是有用的。
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; public class ExceptionTest { public static void main(String[] args) { for(int i =0;i<10;i++) testValidConnection(); while(true);//不让程序结束,能够在数据库中看到数据库链接并无关闭 } public static boolean testValidConnection() { Connection connection = null; try { Class.forName("com.mysql.jdbc.Driver"); connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "tim", "123456"); Statement statement = connection.createStatement(); statement.executeQuery("select *");//错误的SQL语句,抛出异常 connection.close();//并无执行到 } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }finally{ // if(connection != null) // try { // connection.close(); // } catch (SQLException e) { // // ignore // } } return false; } }
如上面的程序所示,SQLException也是一个受检异常,在上面的程序中咱们没有作任何的处理,咱们就把当作非受检异常同样没有捕获异常。咱们只是调用了这个方法10次,以下图所示,使用MySql客户端工具执行show processlist能够看到每一次调用都会创建一个链接而且没有关闭。这样早晚会形成资源耗尽的状况。
图2 数据库链接
因此对于异常的使用建议是尽可能不要使用受检异常,除非特别强调须要API调用者必须执行一些清理工做。对于异常的其余最佳实践推荐《effective java》下面是一张关于异常的知识的一些整理。
图3 异常知识点整理