[14-03] 示例:利用匿名內部類簡化代碼
阿新 • • 發佈:2018-01-20
state value 日誌輸出 try sys 異常處理 黃色 ext setname
內部類的其中一個優勢就是可以簡化代碼,現在以一個常用的JDBC獲取數據封裝對象的例子,來簡單談談如何使用匿名內部類來簡化代碼。
下面這段代碼,是用JDBC連接,到數據庫查詢到數據之後,將數據封裝到對象中進行返回,很常見的場景:
可以看到,使用JDBC連接獲取數據的話,重復的代碼很多(如上圖黃色標註的部分),數據連接,獲取語句對象,執行查詢操作,抓取異常處理,以及關閉連接。這部分代碼完全沒必要每次都寫,想想看,每寫一次類似的獲取數據的方法,這些代碼要copy一次,也是很麻煩的了。
設想一下,我能不能寫一個方法,把這些重復的代碼寫好,每次只需要傳入需要變動的那部分就行了。那麽問題來了,變動的部分,如果只是sql,那還行,畢竟是String字符串,可以作為形參傳入;那我 while(rs.next()) 中的執行代碼也想傳入,怎麽辦?Java是不像JS那種可以把函數作為參數的,只能是對象。
沒關系,我們先按這個思路試一下,把重復的代碼單獨寫在一個方法中:
可以看到,由此以來,我只需要調用 executeQuery(Connection conn, String sql, SqlExecute action) 方法,然後需要執行的操作,以SqlExecute的實現類來傳入就可以了。但是,每次都為了一個方法,新建一個類來實現SqlExecute,反而顯得更繁瑣了。幸好,我們有匿名內部類,實際上,我們使用的時候,會變成這樣。還是以文章前面的JDBC連接為示例:
如上方式可以看到,相當於直接把變動的代碼作為了參數傳入方法。而好處在於,代碼變得簡潔直觀,setParam() 部分就是設置預編譯語句對象的值,而next() 就是在對結果集每一行要執行的操作,一目了然,如果後續需要代碼變動,在哪裏改就可以迅速找到,同時也不需要在此處去特別使用try catch包括代碼和處理異常。另外,在executeQuery方法中,順便還加入了日誌打印的方法,因此你也不必再單獨每個方法去寫一些日誌輸出的方法了。
最後,值得一提的是,因為內部類的原理,所以這裏可以看到,傳參的基本數據類型(如departmentId),以及非內部類中的引用數據類型(對象,如memberList),都必須標註為final修飾,這個在內部類的基礎知識點鐘已經提到,這裏就不再過多闡述了。
內部類的其中一個優勢就是可以簡化代碼,現在以一個常用的JDBC獲取數據封裝對象的例子,來簡單談談如何使用匿名內部類來簡化代碼。
下面這段代碼,是用JDBC連接,到數據庫查詢到數據之後,將數據封裝到對象中進行返回,很常見的場景:
public List<DepartmentMember> getMemberByDepartmentId(long departmentId) {
List<DepartmentMember> memberList = new ArrayList<DepartmentMember>();
Connection conn =
null;PreparedStatement ps = null;
ResultSet rs = null;
String sql = "SELECT m.id,m.name,m.phone,d.value " +
"FROM t_department_member m LEFT JOIN p_dictionary d ON m.job_id = d.id WHERE m.department_id=?";
try {
conn = DBUtil.getConnection();
ps = conn.prepareStatement
(sql);ps.setLong(1, departmentId);
rs = ps.executeQuery();
while (rs.next()) {
DepartmentMember member = new DepartmentMember();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
member.setPhone(rs.getString("phone"
));member.setJob_name(rs.getString("value"));
memberList.add(member);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.closeConnection(conn, ps, rs);
}
return memberList;
}
可以看到,使用JDBC連接獲取數據的話,重復的代碼很多(如上圖黃色標註的部分),數據連接,獲取語句對象,執行查詢操作,抓取異常處理,以及關閉連接。這部分代碼完全沒必要每次都寫,想想看,每寫一次類似的獲取數據的方法,這些代碼要copy一次,也是很麻煩的了。
設想一下,我能不能寫一個方法,把這些重復的代碼寫好,每次只需要傳入需要變動的那部分就行了。那麽問題來了,變動的部分,如果只是sql,那還行,畢竟是String字符串,可以作為形參傳入;那我 while(rs.next()) 中的執行代碼也想傳入,怎麽辦?Java是不像JS那種可以把函數作為參數的,只能是對象。
沒關系,我們先按這個思路試一下,把重復的代碼單獨寫在一個方法中:
- 為了能讓支持其他數據庫連接,我們把連接對象作為方法參數傳入
- 為了傳入變動代碼,我們需要設定一個抽象類作為參數,在方法中執行其方法
public void executeQuery(Connection conn, String sql, SqlExecute action) {
long start = System.currentTimeMillis();
log.debug("execute query start, sql:");
log.debug(sql);
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
//set prepared sql params
action.setParam(ps);
rs = ps.executeQuery();
while (rs.next()) {
//do things while resultset.next()
action.next(rs);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.closeConnection(conn, ps, rs);
}
long end = System.currentTimeMillis();
log.debug("execute query end, cost time:" + (end - start) + "ms");
}
public abstract class SqlExecute {
/**
* 預編譯語句對象賦值
*
* @param ps PreparedStatement預編譯語句對象
*/
void setParam(PreparedStatement ps) throws SQLException {
//for override
}
/**
* 執行操作
*
* @param rs ResultSet結果集
*/
void next(ResultSet rs) throws SQLException {
//for override
}
}
可以看到,由此以來,我只需要調用 executeQuery(Connection conn, String sql, SqlExecute action) 方法,然後需要執行的操作,以SqlExecute的實現類來傳入就可以了。但是,每次都為了一個方法,新建一個類來實現SqlExecute,反而顯得更繁瑣了。幸好,我們有匿名內部類,實際上,我們使用的時候,會變成這樣。還是以文章前面的JDBC連接為示例:
public List<DepartmentMember> getMemberByDepartmentId(final long departmentId) {
final List<DepartmentMember> memberList = new ArrayList<DepartmentMember>();
String sql = "SELECT m.id,m.name,m.phone,d.value " +
"FROM t_department_member m LEFT JOIN p_dictionary d ON m.job_id = d.id WHERE m.department_id=?";
executeQuery(DBUtil.getConnection(), sql, new SqlExecute() {
@Override
void setParam(PreparedStatement ps) throws SQLException {
ps.setLong(1, departmentId);
}
@Override
void next(ResultSet rs) throws SQLException {
DepartmentMember member = new DepartmentMember();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
member.setPhone(rs.getString("phone"));
member.setJob_name(rs.getString("value"));
memberList.add(member);
}
});
return memberList;
}
如上方式可以看到,相當於直接把變動的代碼作為了參數傳入方法。而好處在於,代碼變得簡潔直觀,setParam() 部分就是設置預編譯語句對象的值,而next() 就是在對結果集每一行要執行的操作,一目了然,如果後續需要代碼變動,在哪裏改就可以迅速找到,同時也不需要在此處去特別使用try catch包括代碼和處理異常。另外,在executeQuery方法中,順便還加入了日誌打印的方法,因此你也不必再單獨每個方法去寫一些日誌輸出的方法了。
最後,值得一提的是,因為內部類的原理,所以這裏可以看到,傳參的基本數據類型(如departmentId),以及非內部類中的引用數據類型(對象,如memberList),都必須標註為final修飾,這個在內部類的基礎知識點鐘已經提到,這裏就不再過多闡述了。
[14-03] 示例:利用匿名內部類簡化代碼