您的位置:首页 > 编程语言 > Java开发

利用JAVA反射机制实现调用私有方法

2011-11-07 08:57 573 查看
这两天研究如何利用程序挂断电话。发现在Android1.0的时候Phone中提供了提供了endCall方法,而1.5以后这个方法被设置为私有了。

如此有了如下研究:

/**

* 利用JAVA反射机制调用ITelephony的endCall()结束通话。

*/

private void endCall() {

// 初始化iTelephony

Class<TelephonyManager> c = TelephonyManager.class;

Method getITelephonyMethod = null;

try {

// 获取所有public/private/protected/默认

// 方法的函数,如果只需要获取public方法,则可以调用getMethod.

getITelephonyMethod = c.getDeclaredMethod("getITelephony",

(Class[]) null);

// 将要执行的方法对象设置是否进行访问检查,也就是说对于public/private/protected/默认

// 我们是否能够访问。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false

// 则指示反射的对象应该实施 Java 语言访问检查。

getITelephonyMethod.setAccessible(true);

} catch (SecurityException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (NoSuchMethodException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

try {

ITelephony iTelephony = (ITelephony) getITelephonyMethod.invoke(

tManager, (Object[]) null);

try {

iTelephony.endCall();

} catch (RemoteException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

} catch (IllegalArgumentException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IllegalAccessException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (InvocationTargetException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

/******************************************************************************************/

以下摘自---百度空间---描述了如何利用反射进行私有方法的单元测试

/******************************************************************************************/

利用安全管理器

安全性管理器与反射机制相结合,也可以达到我们的目的。Java 运行时依靠一种安全性管理器来检验调用代码对某一特定的访问而言是否有足够的权限。具体来说,安全性管理器是 java.lang.SecurityManager 类或扩展自该类的一个类,且它在运行时检查某些应用程序操作的权限。换句话说,所有的对象访问在执行自身逻辑之前都必须委派给安全管理器,当访问受到安全性管理器的控制,应用程序就只能执行那些由相关安全策略特别准许的操作。因此安全管理器一旦启动可以为代码提供足够的保护。默认情况下,安全性管理器是没有被设置的,除非代码明确地安装一个默认的或定制的安全管理器,否则运行时的访问控制检查并不起作用。我们可以通过这一点在运行时避开
Java 的访问控制检查,达到我们访问非公有成员变量或方法的目的。为能访问我们需要的非公有成员,我们还需要使用 Java 反射技术。Java 反射是一种强大的工具,它使我们可以在运行时装配代码,而无需在对象之间进行源代码链接,从而使代码更具灵活性。在编译时,Java 编译程序保证了私有成员的私有特性,从而一个类的私有方法和私有成员变量不能被其他类静态引用。然而,通过 Java 反射机制使得我们可以在运行时查询以及访问变量和方法。由于反射是动态的,因此编译时的检查就不再起作用了。

下面的代码演示了如何利用安全性管理器与反射机制访问私有变量。

清单 3. 利用反射机制访问类的成员变量


//获得指定变量的值


public
static Object getValue(Object instance, String fieldName)




throws
IllegalAccessException, NoSuchFieldException ...{




Field field
= getField(instance.getClass(), fieldName);


// 参数值为true,禁用访问控制检查


field.setAccessible(true);


return field.get(instance);


}




//该方法实现根据变量名获得该变量的值


public
static Field getField(Class thisClass, String fieldName)




throws
NoSuchFieldException ...{






if
(thisClass ==
null)
...{


throw
new NoSuchFieldException("Error field !");


}


}



其中 getField(instance.getClass(), fieldName) 通过反射机制获得对象属性,如果存在安全管理器,方法首先使用 this 和 Member.DECLARED 作为参数调用安全管理器的 checkMemberAccess 方法,这里的 this 是 this 类或者成员被确定的父类。 如果该类在包中,那么方法还使用包名作为参数调用安全管理器的 checkPackageAccess 方法。 每一次调用都可能导致 SecurityException。当访问被拒绝时,这两种调用方式都会产生
securityexception 异常 。

setAccessible(true) 方法通过指定参数值为 true 来禁用访问控制检查,从而使得该变量可以被其他类调用。我们可以在我们所写的类中,扩展一个普通的基本类 java.lang.reflect.AccessibleObject 类。这个类定义了一种 setAccessible 方法,使我们能够启动或关闭对这些类中其中一个类的实例的接入检测。这种方法的问题在于如果使用了安全性管理器,它将检测正在关闭接入检测的代码是否允许这样做。如果未经允许,安全性管理器抛出一个例外。

除访问私有变量,我们也可以通过这个方法访问私有方法。

清单 4. 利用反射机制访问类的成员方法


public
static Method getMethod(Object instance, String methodName, Class[] classTypes)




throws
NoSuchMethodException ...{




Method accessMethod
= getMethod(instance.getClass(), methodName, classTypes);


//参数值为true,禁用访问控制检查


accessMethod.setAccessible(true);




return accessMethod;


}




private
static Method getMethod(Class thisClass, String methodName, Class[] classTypes)




throws
NoSuchMethodException ...{






if
(thisClass ==
null)
...{


throw
new NoSuchMethodException("Error method !");




}
try
...{


return thisClass.getDeclaredMethod(methodName, classTypes);




}
catch (NoSuchMethodException e)
...{


return getMethod(thisClass.getSuperclass(), methodName, classTypes);




}


}



获得私有方法的原理与获得私有变量的方法相同。当我们得到了函数后,需要对它进行调用,这时我们需要通过 invoke() 方法来执行对该函数的调用,代码示例如下:


//调用含单个参数的方法


public
static Object invokeMethod(Object instance, String methodName, Object arg)


throws NoSuchMethodException,




IllegalAccessException,
InvocationTargetException ...{




Object[] args
= new Object[1];


args[0]
= arg;


return invokeMethod(instance, methodName, args);


}




//调用含多个参数的方法


public
static Object invokeMethod(Object instance, String methodName, Object[] args)


throws NoSuchMethodException,




IllegalAccessException,
InvocationTargetException ...{


Class[] classTypes
= null;




if
(args !=
null) ...{


classTypes
= new Class[args.length];




for
(int i
= 0; i
< args.length; i++)
...{




if
(args[i] !=
null)
...{


classTypes[i]
= args[i].getClass();


}


}


}


return getMethod(instance, methodName, classTypes).invoke(instance, args);


}



利用安全管理器及反射,可以在不修改源码的基础上访问私有成员,为测试带来了极大的方便。尤其是在编译期间,该方法可以顺利地通过编译。但同时该方法也有一些缺点。第一个是性能问题,用于字段和方法接入时反射要远慢于直接代码。第二个是权限问题,有些涉及 Java 安全的程序代码并没有修改安全管理器的权限,此时本方法失效。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: