1. 程式人生 > >[14-03] 示例:利用匿名內部類簡化代碼

[14-03] 示例:利用匿名內部類簡化代碼

state value 日誌輸出 try sys 異常處理 黃色 ext setname


內部類的其中一個優勢就是可以簡化代碼,現在以一個常用的JDBC獲取數據封裝對象的例子,來簡單談談如何使用匿名內部類來簡化代碼。
下面這段代碼,是用JDBC連接,到數據庫查詢到數據之後,將數據封裝到對象中進行返回,很常見的場景
  1. public List<DepartmentMember> getMemberByDepartmentId(long departmentId) {
  2. List<DepartmentMember> memberList = new ArrayList<DepartmentMember>();
  3. Connection conn =
    null;
  4. PreparedStatement ps = null;
  5. ResultSet rs = null;
  6. String sql = "SELECT m.id,m.name,m.phone,d.value " +
  7. "FROM t_department_member m LEFT JOIN p_dictionary d ON m.job_id = d.id WHERE m.department_id=?";

  8. try {
  9. conn = DBUtil.getConnection();
  10. ps = conn.prepareStatement
    (sql);
  11. ps.setLong(1, departmentId);
  12. rs = ps.executeQuery();
  13. while (rs.next()) {
  14. DepartmentMember member = new DepartmentMember();
  15. member.setId(rs.getLong("id"));
  16. member.setName(rs.getString("name"));
  17. member.setPhone(rs.getString("phone"
    ));
  18. member.setJob_name(rs.getString("value"));
  19. memberList.add(member);
  20. }

  21. } catch (SQLException e) {
  22. e.printStackTrace();
  23. } finally {
  24. DBUtil.closeConnection(conn, ps, rs);
  25. }

  26. return memberList;
  27. }

可以看到,使用JDBC連接獲取數據的話,重復的代碼很多(如上圖黃色標註的部分),數據連接,獲取語句對象,執行查詢操作,抓取異常處理,以及關閉連接。這部分代碼完全沒必要每次都寫,想想看,每寫一次類似的獲取數據的方法,這些代碼要copy一次,也是很麻煩的了。
設想一下,我能不能寫一個方法,把這些重復的代碼寫好,每次只需要傳入需要變動的那部分就行了。那麽問題來了,變動的部分,如果只是sql,那還行,畢竟是String字符串,可以作為形參傳入;那我 while(rs.next()) 中的執行代碼也想傳入,怎麽辦?Java是不像JS那種可以把函數作為參數的,只能是對象。
沒關系,我們先按這個思路試一下,把重復的代碼單獨寫在一個方法中:
  • 為了能讓支持其他數據庫連接,我們把連接對象作為方法參數傳入
  • 為了傳入變動代碼,我們需要設定一個抽象類作為參數,在方法中執行其方法
  1. public void executeQuery(Connection conn, String sql, SqlExecute action) {
  2. long start = System.currentTimeMillis();
  3. log.debug("execute query start, sql:");
  4. log.debug(sql);
  5. PreparedStatement ps = null;
  6. ResultSet rs = null;
  7. try {
  8. ps = conn.prepareStatement(sql);
  9. //set prepared sql params
  10. action.setParam(ps);
  11. rs = ps.executeQuery();
  12. while (rs.next()) {
  13. //do things while resultset.next()
  14. action.next(rs);
  15. }
  16. } catch (SQLException e) {
  17. e.printStackTrace();
  18. } finally {
  19. DBUtil.closeConnection(conn, ps, rs);
  20. }
  21. long end = System.currentTimeMillis();
  22. log.debug("execute query end, cost time:" + (end - start) + "ms");
  23. }
  1. public abstract class SqlExecute {
  2. /**
  3. * 預編譯語句對象賦值
  4. *
  5. * @param ps PreparedStatement預編譯語句對象
  6. */
  7. void setParam(PreparedStatement ps) throws SQLException {
  8. //for override
  9. }
  10. /**
  11. * 執行操作
  12. *
  13. * @param rs ResultSet結果集
  14. */
  15. void next(ResultSet rs) throws SQLException {
  16. //for override
  17. }
  18. }

可以看到,由此以來,我只需要調用 executeQuery(Connection conn, String sql, SqlExecute action) 方法,然後需要執行的操作,以SqlExecute的實現類來傳入就可以了。但是,每次都為了一個方法,新建一個類來實現SqlExecute,反而顯得更繁瑣了。幸好,我們有匿名內部類,實際上,我們使用的時候,會變成這樣。還是以文章前面的JDBC連接為示例:
  1. public List<DepartmentMember> getMemberByDepartmentId(final long departmentId) {
  2. final List<DepartmentMember> memberList = new ArrayList<DepartmentMember>();
  3. String sql = "SELECT m.id,m.name,m.phone,d.value " +
  4. "FROM t_department_member m LEFT JOIN p_dictionary d ON m.job_id = d.id WHERE m.department_id=?";
  5. executeQuery(DBUtil.getConnection(), sql, new SqlExecute() {
  6. @Override
  7. void setParam(PreparedStatement ps) throws SQLException {
  8. ps.setLong(1, departmentId);
  9. }
  10. @Override
  11. void next(ResultSet rs) throws SQLException {
  12. DepartmentMember member = new DepartmentMember();
  13. member.setId(rs.getLong("id"));
  14. member.setName(rs.getString("name"));
  15. member.setPhone(rs.getString("phone"));
  16. member.setJob_name(rs.getString("value"));
  17. memberList.add(member);
  18. }
  19. });
  20. return memberList;
  21. }

如上方式可以看到,相當於直接把變動的代碼作為了參數傳入方法。而好處在於,代碼變得簡潔直觀,setParam() 部分就是設置預編譯語句對象的值,而next() 就是在對結果集每一行要執行的操作,一目了然,如果後續需要代碼變動,在哪裏改就可以迅速找到,同時也不需要在此處去特別使用try catch包括代碼和處理異常。另外,在executeQuery方法中,順便還加入了日誌打印的方法,因此你也不必再單獨每個方法去寫一些日誌輸出的方法了。
最後,值得一提的是,因為內部類的原理,所以這裏可以看到,傳參的基本數據類型(如departmentId),以及非內部類中的引用數據類型(對象,如memberList),都必須標註為final修飾,這個在內部類的基礎知識點鐘已經提到,這裏就不再過多闡述了。

[14-03] 示例:利用匿名內部類簡化代碼