前面介绍了如何利用反射技术读写私有属性,不单是私有属性,就连私有方法也能经过反射技术来调用。为了演示反射的逆天功能,首先给Chicken鸡类增长下列几个私有方法,简单起见弄来了set***/get***这样的基本方法:html
private void setName(String name) { // 设置名称 this.name = name; } private String getName() { // 获取名称 return this.name; } private void setSex(int sex) { // 设置性别 this.sex = sex; } private int getSex() { // 获取性别 return this.sex; }
参照私有属性的反射操做过程,私有方法的反射调用可分解为以下三个步骤:java
一、调用Class对象的getDeclaredMethod方法,获取指定名称的方法对象,即Method对象;
二、调用Method对象的setAccessible方法,并传入true值,表示将该方法设置为容许访问,以解除private的限制;
三、调用Method对象的invoke方法,并传入鸡类实例,酌情填写输入参数;
虽然方法只有调用一说,没有读写之分,可是方法的输入参数能够有也能够没有,一样输出参数能够有也能够没有,于是对于方法对象而言,反射技术须要支持如下四种状况:有输入参数、无输入参数、有输出参数、无输出参数。注意到Chicken类的新增方法getName无输入参数、有输出参数,setName有输入参数、无输出参数,故而只要实现getName与setName两个方法的反射调用,恰好就覆盖了有无入参和有无出参这四种场景。
先来看getName,由于该方法没有输入参数,因此反射调用相对简单,只是invoke方法的返回值为Object类型,须要强制转换成String类型,这样才能得到鸡的名称。此时获取名称的反射代码以下所示:this
// 经过反射来调用某个实例的私有方法(getName方法) private static String getReflectName(Chicken chicken) { String name = ""; try { Class cls = Chicken.class; // 得到Chicken类的基因类型 // 经过方法名称及参数列表获取该方法的Method对象 Method method = cls.getDeclaredMethod("getName"); method.setAccessible(true); // 将该方法设置为容许访问 name = (String) method.invoke(chicken); // 调用某实例的方法并得到输出参数 } catch (Exception e) { // 捕捉到了任何一种异常(错误除外) e.printStackTrace(); } return name; }
再来看setName,因为该方法存在输入参数,所以调用Class对象的getDeclaredMethod之时,须要传入参数类型列表。之因此这么作,是由于同名方法可能会被屡次重载,重载后的方法经过参数个数与参数类型加以区分。另外,invoke方法也要传入setName方法所需的各项参数值。一系列调整以后,设置名称的反射代码改写以下:日志
// 经过反射来调用某个实例的私有方法(setName方法) private static void setReflectName(Chicken chicken, String name) { try { Class cls = Chicken.class; // 得到Chicken类的基因类型 // 经过方法名称及参数列表获取该方法的Method对象 // 之因此须要参数类型列表,是由于同名方法可能会被屡次重载,重载后的方法经过参数个数与参数类型加以区分 Method method = cls.getDeclaredMethod("setName", String.class); method.setAccessible(true); // 将该方法设置为容许访问 method.invoke(chicken, name); // 携带输入参数调用某实例的方法 } catch (Exception e) { // 捕捉到了任何一种异常(错误除外) e.printStackTrace(); } }
编写完成名称获取与名称设置的反射代码,获取性别与设置性别的反射代码便可如法炮制,区别主要有两处,一处的强制类型转换把“(int)”换成“(String)”,另外一处的参数类型列表把“String.class”换成“int.class”。性别获取与性别设置的反射代码示例以下:htm
// 经过反射来调用某个实例的私有方法(getSex方法) private static int getReflectSex(Chicken chicken) { int sex = -1; try { Class cls = Chicken.class; // 得到Chicken类的基因类型 // 经过方法名称及参数列表获取该方法的Method对象 Method method = cls.getDeclaredMethod("getSex"); method.setAccessible(true); // 将该方法设置为容许访问 sex = (int) method.invoke(chicken); // 调用某实例的方法并得到输出参数 } catch (Exception e) { // 捕捉到了任何一种异常(错误除外) e.printStackTrace(); } return sex; } // 经过反射来调用某个实例的私有方法(setSex方法) private static void setReflectSex(Chicken chicken, int sex) { try { Class cls = Chicken.class; // 得到Chicken类的基因类型 // 经过方法名称及参数列表获取该方法的Method对象 // 之因此须要参数类型列表,是由于同名方法可能会被屡次重载,重载后的方法经过参数个数与参数类型加以区分 Method method = cls.getDeclaredMethod("setSex", int.class); method.setAccessible(true); // 将该方法设置为容许访问 method.invoke(chicken, sex); // 携带输入参数调用某实例的方法 } catch (Exception e) { // 捕捉到了任何一种异常(错误除外) e.printStackTrace(); } }
而后轮到外部调用这几个封装好的反射方法了,准备把公鸡实例的名称改成“母鸭”,性别改成“雌性”,具体的调用代码以下所示:对象
Cock cock = new Cock(); // 建立一个公鸡实例 System.out.println("准备修理公鸡, 名称 = "+getReflectName(cock)+", 性别 = "+getReflectSex(cock)); setReflectName(cock, "母鸭"); // 把公鸡实例的名称篡改成“母鸭” setReflectSex(cock, cock.FEMALE); // 把公鸡实例的性别篡改成“雌性” System.out.println("结束修理公鸡, 名称 = "+getReflectName(cock)+", 性别 = "+getReflectSex(cock));
运行上面的演示代码,观察到下面的日志信息,可见一只雄赳赳的公鸡被硬生生整成了母鸭模样:blog
准备修理公鸡, 名称 = 公鸡, 性别 = 0 结束修理公鸡, 名称 = 母鸭, 性别 = 1
更多Java技术文章参见《Java开发笔记(序)章节目录》开发