Java 反射中的getDeclaredMethod(String,Class?[])与NoSuchMethodExe

前几天笔者在写了一个比较简陋的底层数据库封装类,但测试时出现NoSuchMethodException.下面是部分源代码..

/*** 对数据库的查询<br/>* 注意:使用时,表的列名必须与实体对应的属性名一致.** @param sql*SQL语句* @param params*注入参数* @param clazz*对象类的模板* @return 对象类的集合,没有结果时size()为0.*/public static <T> List<T> executeQuery(String sql, Object[] params,Class<T> clazz) {// 结果集合ArrayList<T> result = new ArrayList<T>();// 获取ConnectionConnection con = getConnection();// 声明PreparedStatementPreparedStatement ps = null;// 声明结果集ResultSet rs = null;// 如果连接不为空if (con != null) {try {// 注入SQLps = con.prepareStatement(sql);// 注入参数insertParams(ps, params);// 获取结果集rs = ps.executeQuery();// 获取结果集元数据ResultSetMetaData rsmd = rs.getMetaData();// 获取列的信息HashMap<String, Integer> columnInfo = getColumnInfo(rsmd);// 获取列名Set<String> columnNames = columnInfo.keySet();// 循环结果集while (rs.next()) {// 实例化一个泛型对象T t = clazz.newInstance();// 定义为Object类型,以至于可以接收多种数据类型Object value = null;for (String columnName : columnNames) {// 获取列的类型,调用ResultSet对应的set方法来获取valueint type = columnInfo.get(columnName);switch (type) {case Types.INTEGER:value = rs.getInt(columnName);break;case Types.DOUBLE:// SQL中的Float与Types.Double对应value = rs.getDouble(columnName);break;case Types.VARCHAR:value = rs.getString(columnName);break;case Types.TIMESTAMP:value = rs.getTimestamp(columnName);default:value = rs.getObject(columnName);break;}String name = “set” + UpperFirstCase(columnName);// 获取泛型实例对象的对应方法并执行Method method = null;method = clazz.getDeclaredMethod(name,new Class<?>[] { value.getClass() });method.invoke(t, value);}// 将此泛型实例添加到结果集合中result.add(t);}} catch (SQLException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (SecurityException e) {e.printStackTrace();} catch (InvocationTargetException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {closeAll(con, ps, rs);}}return result;}

经过调试后发现问题出现在65行的getDeclaredMethod(String,Class<?>[])方法中,在调试过程中还发现该方法可以反射并执行getXXX()方法.但在反射setXXX(xxx,xxxx)方法时,反射的方法名是正确的,于是把错误圈定在参数类型上.

在一系列测试后发现,基本数据类型存放到Object后再取出来会变成其对应的封装类,而getDeclaredMethod(String,Class<?>[])方法又区分基本类型和其对应的封装类.

经过一翻探究后想出了三种解决方法:

使用getDeclaredMethods()方法,通过遍历的方式对方法名进行匹配,可在循环内部添加条件来提高匹配精度.但效率比较低.

约定类的方法的参数类型不能使用基本类型,只能用其对应的封装类(别笑…这种方法很有效…其实我当时忍不住要笑…)

(也就是我最后采取的方法).通过以下方法,传入Object类,尝试获得基本类型.

/*** 由于反射的getDeclaredMethod()方法不支持基本类型,所以用这个方法进行转换** @param o* @return*/private static Class<?> changeToBasicClass(Object o) {Class<?> clazz = o.getClass();if (clazz == Integer.class) {clazz = Integer.TYPE;} else if (clazz == Long.class) {clazz = Long.TYPE;} else if (clazz == Float.class) {clazz = Float.TYPE;} else if (clazz == Double.class) {clazz = Double.TYPE;} else if (clazz == Short.class) {clazz = Short.TYPE;} else if (clazz == Boolean.class) {clazz = Boolean.TYPE;} else if (clazz == Character.class) {clazz = Character.TYPE;} else if (clazz == Byte.class) {clazz = Byte.TYPE;}return clazz;}

下面贴上笔者修改后的代码.

/*** 对数据库的查询<br/>* 注意:使用时,表的列名必须与实体对应的属性名一致.** @param sql*SQL语句* @param params*注入参数* @param clazz*对象类的模板* @return 对象类的集合,没有结果时size()为0.*/public static <T> List<T> executeQuery(String sql, Object[] params,Class<T> clazz) {// 结果集合ArrayList<T> result = new ArrayList<T>();// 获取ConnectionConnection con = getConnection();// 声明PreparedStatementPreparedStatement ps = null;// 声明结果集ResultSet rs = null;// 如果连接不为空if (con != null) {try {// 注入SQLps = con.prepareStatement(sql);// 注入参数insertParams(ps, params);// 获取结果集rs = ps.executeQuery();// 获取结果集元数据ResultSetMetaData rsmd = rs.getMetaData();// 获取列的信息HashMap<String, Integer> columnInfo = getColumnInfo(rsmd);// 获取列名Set<String> columnNames = columnInfo.keySet();// 循环结果集while (rs.next()) {// 实例化一个泛型对象T t = clazz.newInstance();// 定义为Object类型,以至于可以接收多种数据类型Object value = null;for (String columnName : columnNames) {// 获取列的类型,调用ResultSet对应的set方法来获取valueint type = columnInfo.get(columnName);switch (type) {case Types.INTEGER:value = rs.getInt(columnName);break;case Types.DOUBLE:// SQL中的Float与Types.Double对应value = rs.getDouble(columnName);break;case Types.VARCHAR:value = rs.getString(columnName);break;case Types.TIMESTAMP:value = rs.getTimestamp(columnName);default:value = rs.getObject(columnName);break;}String name = “set” + UpperFirstCase(columnName);// 获取泛型实例对象的对应方法并执行Method method = null;try {method = clazz.getDeclaredMethod(name,new Class<?>[] { value.getClass() });} catch (NoSuchMethodException e) {// 如果无法获取方法,可能是因为参数是基本类型,调用changeToBasicClass()方法try {method = clazz.getDeclaredMethod(name,new Class<?>[] { changeToBasicClass(value) });} catch (NoSuchMethodException e1) {e1.printStackTrace();}}method.invoke(t, value);}// 将此泛型实例添加到结果集合中result.add(t);}} catch (SQLException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (SecurityException e) {e.printStackTrace();} catch (InvocationTargetException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {closeAll(con, ps, rs);}}return result;}

不过有一点东西很蛋疼,那就是

public void a(int b){}public void a(Integer b){}

在Java看来,这是两个方法不同的方法,它们之间是重载关系….

还有一点小发现…自从在编程这方面发展后…记忆力越来越差了…

本文出自 “异性恋恐惧症” 博客,请务必保留此出处

,分之百的把自己推销给自己。

Java 反射中的getDeclaredMethod(String,Class?[])与NoSuchMethodExe

相关文章:

你感兴趣的文章:

标签云: