今天在程序中用到java反射机制时,遇到的问题记录一下:我当时遇到的问题是,我用反射getMethod()调用类方法时,发生NoSuchMethodException异常,后来上网发现getMethod()调用公共方法,不能反射调用私有方法,后来找到getDeclaredField()能够访问本类中定义的所有方法。后来用这个方法解决了我遇到的问题。我查了java api文档,其中详细说明如下:
Method getDeclaredMethod(String name, Class… parameterTypes)
返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
Method[] getDeclaredMethods()
返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
Method getMethod(String name, Class… parameterTypes)
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
Method[] getMethods()
返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
getDeclaredField(String name)
返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
Field[] getDeclaredFields()
返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段,包括公共、保护、默认(包)访问和私有字段,但不包括继承的字段
private String getFieldValue(ProjectToExcelDTO dto,String [] fields) { Class clazz = dto.getClass(); String methodName; Method method = null; String fieldValue = null; StringBuffer sb = new StringBuffer(); try{ for (String fieldName : fields ){ methodName = "get" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1); method = clazz.getDeclaredMethod(methodName); fieldValue = (String)method.invoke(dto); fieldValue = StringUtils.trimToEmpty(fieldValue); fieldValue = "\"" + fieldValue + "\""; sb.append(fieldValue).append(","); } }catch(Exception e){ e.printStackTrace(); } return sb.toString(); }
本篇文章为在工作中使用JAVA反射 的经验总结,也可以说是一些小技巧,以后学会新的小技巧,会不断更新。本文不准备讨论JAVA反射的机制,网上有很多,大家随便google一下就可以了。 在开始之前,我先定义一个测试类Student,代码如下: 1 package chb.test.reflect; 2 3 public class Student { 4 private int age; 5 private String name; 6 public int getAge() { 7 return age; 8 } 9 public void setAge(int age) { 10 this.age = age; 11 } 12 public String getName() { 13 return name; 14 } 15 public void setName(String name) { 16 this.name = name; 17 } 18 19 public static void hi(int age,String name){ 20 System.out.println("大家好,我叫"+name+",今年"+age+"岁"); 21 } 22 } 一、JAVA反射的常规使用步骤 反射调用一般分为3个步骤: ·得到要调用类的class ·得到要调用的类中的方法(Method) ·方法调用(invoke) 代码示例: 1 Class cls = Class.forName("chb.test.reflect.Student"); 2 Method m = cls.getDeclaredMethod("hi",new Class[]{int.class,String.class}); 3 m.invoke(cls.newInstance(),20,"chb"); 二、方法调用中的参数类型 在方法调用中,参数类型必须正确,这里需要注意的是不能使用包装类替换基本类型,比如不能使用Integer.class代替int.class。 如我要调用Student的setAge方法,下面的调用是正确的 1 Class cls = Class.forName("chb.test.reflect.Student"); 2 Method setMethod = cls.getDeclaredMethod("setAge",int.class); 3 setMethod.invoke(cls.newInstance(), 15); 而如果我们用Integer.class替代int.class就会出错,如: 1 Class cls = Class.forName("chb.test.reflect.Student"); 2 Method setMethod = cls.getDeclaredMethod("setAge",Integer.class); 3 setMethod.invoke(cls.newInstance(), 15); jvm会报出如下异常: 1 java.lang.NoSuchMethodException: chb.test.reflect.Student.setAge(java.lang.Integer) 2 at java.lang.Class.getDeclaredMethod(Unknown Source) 3 at chb.test.reflect.TestClass.testReflect(TestClass.java:23) 三、static方法的反射调用 static方法调用时,不必得到对象示例,如下: 1 Class cls = Class.forName("chb.test.reflect.Student"); 2 Method staticMethod = cls.getDeclaredMethod("hi",int.class,String.class); 3 staticMethod.invoke(cls,20,"chb");//这里不需要newInstance 4 //staticMethod.invoke(cls.newInstance(),20,"chb"); 四、private的成员变量赋值 如果直接通过反射给类的private成员变量赋值,是不允许的,这时我们可以通过setAccessible方法解决。代码示例: 1 Class cls = Class.forName("chb.test.reflect.Student"); 2 Object student = cls.newInstance();//得到一个实例 3 Field field = cls.getDeclaredField("age"); 4 field.set(student, 10); 5 System.out.println(field.get(student)); 运行如上代码,系统会报出如下异常: 1 java.lang.IllegalAccessException: Class chb.test.reflect.TestClass can not access a member of class chb.test.reflect.Student with modifiers "private" 2 at sun.reflect.Reflection.ensureMemberAccess(Unknown Source) 3 at java.lang.reflect.Field.doSecurityCheck(Unknown Source) 4 at java.lang.reflect.Field.getFieldAccessor(Unknown Source) 5 at java.lang.reflect.Field.set(Unknown Source) 6 at chb.test.reflect.TestClass.testReflect(TestClass.java:20) 解决方法: 1 Class cls = Class.forName("chb.test.reflect.Student"); 2 Object student = cls.newInstance(); 3 Field field = cls.getDeclaredField("age"); 4 field.setAccessible(true);//设置允许访问 5 field.set(student, 10); 6 System.out.println(field.get(student)); 其实,在某些场合下(类中有get,set方法),可以先反射调用set方法,再反射调用get方法达到如上效果,代码示例: 1 Class cls = Class.forName("chb.test.reflect.Student"); 2 Object student = cls.newInstance(); 3 4 Method setMethod = cls.getDeclaredMethod("setAge",Integer.class); 5 setMethod.invoke(student, 15);//调用set方法 6 7 Method getMethod = cls.getDeclaredMethod("getAge"); 8 System.out.println(getMethod.invoke(student));//再调用get方法