• [14-03] 示例:利用匿名内部类简化代码



    内部类的其中一个优势就是可以简化代码,现在以一个常用的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修饰,这个在内部类的基础知识点钟已经提到,这里就不再过多阐述了。


  • 相关阅读:
    eval()用法
    TTL查看目标的操作系统
    Windows Mobile里用SQLite的 Pinvoke DLL "SQLite.Interop.DLL 异常处理
    创建56个民族的sql语句
    RSS 入门简介
    NAT 网络地址转换
    ARP 攻击
    Python核心数据类型——列表
    Python核心数据类型——字符串
    Linux下 PyDev + Eclipse安装方法
  • 原文地址:https://www.cnblogs.com/deng-cc/p/8320375.html
Copyright © 2020-2023  润新知