Java开发笔记(八十)利用反射技术操作私有方法
前面介绍了如何利用反射技术读写私有属性,不单是私有属性,就连私有方法也能通过反射技术来调用。为了演示反射的逆天功能,首先给chicken鸡类增加下列几个私有方法,简单起见弄来了set***/get***这样的基本方法:
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;
}
参照私有属性的反射操作过程,私有方法的反射调用可分解为如下三个步骤:
1、调用class对象的getdeclaredmethod方法,获取指定名称的方法对象,即method对象;
2、调用method对象的setaccessible方法,并传入true值,表示将该方法设置为允许访问,以解除private的限制;
3、调用method对象的invoke方法,并传入鸡类实例,酌情填写输入参数;
虽然方法只有调用一说,没有读写之分,但是方法的输入参数可以有也可以没有,同样输出参数可以有也可以没有,因而对于方法对象而言,反射技术需要支持以下四种情况:有输入参数、无输入参数、有输出参数、无输出参数。注意到chicken类的新增方法getname无输入参数、有输出参数,setname有输入参数、无输出参数,故而只要实现getname与setname两个方法的反射调用,刚好就覆盖了有无入参和有无出参这四种场景。
先来看getname,因为该方法没有输入参数,所以反射调用相对简单,只是invoke方法的返回值为object类型,需要强制转换成string类型,这样才能获得鸡的名称。此时获取名称的反射代码如下所示:
// 通过反射来调用某个实例的私有方法(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”。性别获取与性别设置的反射代码示例如下:
// 通过反射来调用某个实例的私有方法(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));
运行上面的演示代码,观察到下面的日志信息,可见一只雄赳赳的公鸡被硬生生整成了母鸭模样:
准备修理公鸡, 名称 = 公鸡, 性别 = 0
结束修理公鸡, 名称 = 母鸭, 性别 = 1
更多java技术文章参见《java开发笔记(序)章节目录》
上一篇: 裁员的小套路
下一篇: Sql Server 的参数化查询