JAVA语言中的反射机制:
Java反射是Java被视为动态(或准动态)语言的一个关键 性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等)、superclass(例如Object)、实现之interfaces(例如Cloneable),也包括fields和methods的所有信 息,并可于运行时改变fields内容或唤起methods。
Java反射机制容许程序在运行时加载、探知、使用编译期间完全未知的classes。
JAVA反射机制主要提供了以下功能:
1.在运行时判断任意一个对象所属的类
2.在运行时构造任意一个类的对象
3.在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法)
4.在运行时调用任意一个对象的方法(*****注意:前提都是在运行时,而不是在编译时)
Java 反射相关的API简介:
位于java。lang.reflect包中
--Class类:代表一个类
--Filed类:代表类的成员变量
--Method类:代表类的方法
--Constructor类:代表类的构造方法
--Array类:提供了动态创建数组,以及访问数组的元素的静态方法。该类中的所有方法都是静态方法
----Class类
在 java 的Object类中的申明了数个应该在所有的java类中被改写的methods:hashCode(), equals(),clone(),toString(),getClass()等,其中的getClass()返回一个Class 类型的对象。
Class类十分的特殊,它和一般的类一样继承自Object,其实体用以表达java程序运行时的 class和 interface,也用来表达 enum,array,primitive,Java Types 以及关键字void,当加载一个类,或者当加载器(class loader)的defineClass()被JVM调用,便产生一个Class对象,
Class是Reflection起源,针对任何你想探勘的class(类),唯有现为他产生一个Class的对象,接下来才能经由后者唤起为数十多个的反射API。
Java允许我们从多种途径为一个类class生成对应的Class对象。
--运用 getClass():Object类中的方法,每个类都拥有此方法
String str="abc";
Class cl=str.getClass();
--运用 Class。getSuperclass():Class类中的方法,返回该Class的父类的Class
--运用 Class。forName()静态方法:
--运用 ,Class:类名.class
--运用primitive wrapper classes的TYPE语法: 基本类型包装类的TYPE,如:Integer.TYPE
注意:TYPE的使用,只适合原生(基本)数据类型
----Field类
public Field getField(String name) 返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段
public Field [] getFields() 返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段
public Field getDeclaredField(String name) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段
public Field [] getDeclaredFields() 返回对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段
可见getFields和getDeclaredFields区别:
getFields返回的是申明为public的属性,包括父类中定义,
getDeclaredFields返回的是指定类定义的所有定义的属性,不包括父类的。
----Method类
通过反射机制得到某个类的某个方法,然后调用对应于这个类的某个实例的该方法
Class<T>类提供了几个方法获取类的方法。
public Method getMethod(String name, Class <?>... parameterTypes)
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法
public Method [] getMethods()
返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法
public Method getDeclaredMethod(String name,Class <?>... parameterTypes)
返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法
public Method [] getDeclaredMethods()
返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法
下面的这个方法时有些缺陷的,通过params 一定能得到正确的 method的 paramTypes吗? 子类可以代替父类,实现类可以代理接口作为参数.这样 params[i].getClass 已经不是method meta里面的 class了.
public void rebindMethod(String methodName, Object ...params) {
this.params = params;
int paramLength = params.length;
Class[] paramTypes = new Class[paramLength];
for(int i = 0 ; i < paramLength ; i ++ ) {
paramTypes[i] = params[i].getClass();
}
try {
this.method = clazz.getMethod(methodName, paramTypes);
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
----运行时生成instance
想生成对象的实体,在反射动态机制中有两种方法,一个针对无变量的构造方法,一个针对带参数的构造方法,,如果想调用带参数的构造方法,就比较的麻烦,不能直接调用Class类中的newInstance(),而是调用Constructor类中newInstance()方法,首先准备一个Class[]作为Constructor的参数类型。然后调用该Class对象的getConstructor()方法获得一个专属的Constructor的对象,最后再准备一个Object[]作为Constructor对象昂的newInstance()方法的实参。
在这里需要说明的是 只有两个类拥有newInstance()方法,分别是Class类和Constructor类Class类中的newInstance()方法是不带参数的,而Constructro类中的newInstance()方法是带参数的需要提供必要的参数。
例:
Class c=Class.forName("DynTest");
Class[] ptype=new Class[]{double.class,int.class};
Constructor ctor=c.getConstructor(ptypr);
Object[] obj=new Object[]{new Double(3.1415),new Integer(123)};
Object object=ctor.newInstance(obj);
System.out.println(object);
更多的方法
通过反射机制得到某个类的构造器,然后调用该构造器创建该类的一个实例
Class<T>类提供了几个方法获取类的构造器。
public Constructor <T > getConstructor(Class <?>... parameterTypes)
返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法
public Constructor <?>[] getConstructors()
返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法
public Constructor <T > getDeclaredConstructor(Class <?>... parameterTypes)
返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法
public Constructor <?>[] getDeclaredConstructors()
返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。它们是公共、保护、默认(包)访问和私有构造方法
下面提供一种利用安全管理器及反射访问类非公有方法和成员的方法。
Java 运行时依靠一种安全性管理器来检验调用代码对某一特定的访问而言是否有足够的权限。具体来说,安全性管理器是 java.lang.SecurityManager 类或扩展自该类的一个类,且它在运行时检查某些应用程序操作的权限。换句话说,所有的对象访问在执行自身逻辑之前都必须委派给安全管理器,当访问受到安全性管理器的控制,应用程序就只能执行那些由相关安全策略特别准许的操作。因此安全管理器一旦启动可以为代码提供足够的保护。默认情况下,安全性管理器是没有被设置的,除非代码明确地安装一个默认的或定制的安全管理器,否则运行时的访问控制检查并不起作用。我们可以通过这一点在运行时避开 Java 的访问控制检查,达到我们访问非公有成员变量或方法的目的。为能访问我们需要的非公有成员,我们还需要使用 Java 反射技术。Java 反射是一种强大的工具,它使我们可以在运行时装配代码,而无需在对象之间进行源代码链接,从而使代码更具灵活性。在编译时,Java 编译程序保证了私有成员的私有特性,从而一个类的私有方法和私有成员变量不能被其他类静态引用。然而,通过 Java 反射机制使得我们可以在运行时查询以及访问变量和方法。由于反射是动态的,因此编译时的检查就不再起作用了。
下面的代码演示了如何利用安全性管理器与反射机制访问私有变量。
// 获得指定变量的值
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);
}
其中 getField(instance.getClass(), fieldName) 通过反射机制获得对象属性,使用set方法可以重新设置变量的值,如field.set(instance, newValue); 。如果存在安全管理器,方法首先使用 this 和 Member.DECLARED 作为参数调用安全管理器的 checkMemberAccess 方法,这里的 this 是 this 类或者成员被确定的父类。 如果该类在包中,那么方法还使用包名作为参数调用安全管理器的 checkPackageAccess 方法。 每一次调用都可能导致 SecurityException。当访问被拒绝时,这两种调用方式都会产生 securityexception 异常 。
setAccessible(true) 方法通过指定参数值为 true 来禁用访问控制检查,从而使得该变量可以被其他类调用。我们可以在我们所写的类中,扩展一个普通的基本类 java.lang.reflect.AccessibleObject 类。这个类定义了一种 setAccessible 方法,使我们能够启动或关闭对这些类中其中一个类的实例的接入检测。这种方法的问题在于如果使用了安全性管理器,它将检测正在关闭接入检测的代码是否允许这样做。如果未经允许,安全性管理器抛出一个例外。
除访问私有变量,我们也可以通过这个方法访问私有方法。
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 安全的程序代码并没有修改安全管理器的权限,此时本方法失效。
关于数组:
如果因为某种原因,您并不确定参数或对象是不是数组,您可以检索对象的 Class 对象并询问它。 Class 类的 isArray() 方法将会告诉您。一旦您知道拥有了一个数组,您可以询问 Class 的 getComponentType() 方法,您实际拥有的是什么类型的数组。如果 isArray() 方法返回 false,那么 getComponentType() 方法返回空。否则返回元素的 Class 类型。如果数组是多维的,您可以递归调用 isArray() 。它将仍只包含一个 component 类型。此外,您可以用在 java.lang.reflect 包里找到的 Array 类的 getLength() 方法获取数组的长度。
为了演示,清单 2-3 显示了传递给 main() 方法的参数是 java.lang.String 对象的数组,其中数组长度由命令行参数的个数确定:
public class ArrayReflection {
public static void main (String args[]) {
printType(args);
}
private static void printType (Object object) {
Class type = object.getClass();
if (type.isArray()) {
Class elementType = type.getComponentType();
System.out.println("Array of: " + elementType);
System.out.println(" Length: " + Array.getLength(object));
}
}
}
如果不使用 isArray() 和 getComponentType() 方法,而且试图打印数组的 Class 类型,您将获得一个包含 [ ,后面跟着一个字母和类名(如果是个基本数据类型就没有类名)的字符串。例如,如果您试图打印出上述 printType() 方法中的类型变量,您将获得 class [Ljava.lang.String; 作为输出。
使用反射创建、填充和显示数组
import java.lang.reflect.Array;
import java.util.Random;
public class ArrayCreate {
public static void main (String args[]) {
Object array = Array.newInstance(int.class, 3);
printType(array);
fillArray(array);
displayArray(array);
}
private static void printType (Object object) {
Class type = object.getClass();
if (type.isArray()) {
Class elementType = type.getComponentType();
System.out.println("Array of: " + elementType);
System.out.println("Array size: " + Array.getLength(object));
}
}
private static void fillArray(Object array) {
int length = Array.getLength(array);
Random generator = new Random(System.currentTimeMillis());
for (int i=0; i<length; i++) {
int random = generator.nextInt();
Array.setInt(array, i, random);
}
}
private static void displayArray(Object array) {
int length = Array.getLength(array);
for (int i=0; i<length; i++) {
int value = Array.getInt(array, i);
System.out.println("Position: " + i +", value: " + value);
}
}
}
运行时,输出将如下所示(尽管随机数会不同):
Array of: int
Array size: 3
Position: 0, value: -54541791
Position: 1, value: -972349058
Position: 2, value: 1224789416
在android里面的一个应用
ITelephony.aidl里面有很多的方法,可以通过反射调用,自动接电话,挂电话,断网等。
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.telephony;
import android.os.Bundle;
import java.util.List;
/**
* Interface used to interact with the phone. Mostly this is used by the
* TelephonyManager class. A few places are still using this directly.
* Please clean them up if possible and use TelephonyManager insteadl.
*
* {@hide}
*/
interface ITelephony {
/**
* Dial a number. This doesn't place the call. It displays
* the Dialer screen.
* @param number the number to be dialed. If null, this
* would display the Dialer screen with no number pre-filled.
*/
void dial(String number);
/**
* Place a call to the specified number.
* @param number the number to be called.
*/
void call(String number);
/**
* If there is currently a call in progress, show the call screen.
* The DTMF dialpad may or may not be visible initially, depending on
* whether it was up when the user last exited the InCallScreen.
*
* @return true if the call screen was shown.
*/
boolean showCallScreen();
/**
* Variation of showCallScreen() that also specifies whether the
* DTMF dialpad should be initially visible when the InCallScreen
* comes up.
*
* @param showDialpad if true, make the dialpad visible initially,
* otherwise hide the dialpad initially.
* @return true if the call screen was shown.
*
* @see showCallScreen
*/
boolean showCallScreenWithDialpad(boolean showDialpad);
/**
* End call or go to the Home screen
*
* @return whether it hung up
*/
boolean endCall();
/**
* Answer the currently-ringing call.
*
* If there's already a current active call, that call will be
* automatically put on hold. If both lines are currently in use, the
* current active call will be ended.
*
* TODO: provide a flag to let the caller specify what policy to use
* if both lines are in use. (The current behavior is hardwired to
* "answer incoming, end ongoing", which is how the CALL button
* is specced to behave.)
*
* TODO: this should be a oneway call (especially since it's called
* directly from the key queue thread).
*/
void answerRingingCall();
/**
* Silence the ringer if an incoming call is currently ringing.
* (If vibrating, stop the vibrator also.)
*
* It's safe to call this if the ringer has already been silenced, or
* even if there's no incoming call. (If so, this method will do nothing.)
*
* TODO: this should be a oneway call too (see above).
* (Actually *all* the methods here that return void can
* probably be oneway.)
*/
void silenceRinger();
/**
* Check if we are in either an active or holding call
* @return true if the phone state is OFFHOOK.
*/
boolean isOffhook();
/**
* Check if an incoming phone call is ringing or call waiting.
* @return true if the phone state is RINGING.
*/
boolean isRinging();
/**
* Check if the phone is idle.
* @return true if the phone state is IDLE.
*/
boolean isIdle();
/**
* Check to see if the radio is on or not.
* @return returns true if the radio is on.
*/
boolean isRadioOn();
/**
* Check if the SIM pin lock is enabled.
* @return true if the SIM pin lock is enabled.
*/
boolean isSimPinEnabled();
/**
* Cancels the missed calls notification.
*/
void cancelMissedCallsNotification();
/**
* Supply a pin to unlock the SIM. Blocks until a result is determined.
* @param pin The pin to check.
* @return whether the operation was a success.
*/
boolean supplyPin(String pin);
/**
* Handles PIN MMI commands (PIN/PIN2/PUK/PUK2), which are initiated
* without SEND (so <code>dial</code> is not appropriate).
*
* @param dialString the MMI command to be executed.
* @return true if MMI command is executed.
*/
boolean handlePinMmi(String dialString);
/**
* Toggles the radio on or off.
*/
void toggleRadioOnOff();
/**
* Set the radio to on or off
*/
boolean setRadio(boolean turnOn);
/**
* Request to update location information in service state
*/
void updateServiceLocation();
/**
* Enable location update notifications.
*/
void enableLocationUpdates();
/**
* Disable location update notifications.
*/
void disableLocationUpdates();
/**
* Enable a specific APN type.
*/
int enableApnType(String type);
/**
* Disable a specific APN type.
*/
int disableApnType(String type);
/**
* Allow mobile data connections.
*/
boolean enableDataConnectivity();
/**
* Disallow mobile data connections.
*/
boolean disableDataConnectivity();
/**
* Report whether data connectivity is possible.
*/
boolean isDataConnectivityPossible();
Bundle getCellLocation();
/**
* Returns the neighboring cell information of the device.
*/
int getCallState();
int getDataActivity();
int getDataState();
/**
* Returns the current active phone type as integer.
* Returns TelephonyManager.PHONE_TYPE_CDMA if RILConstants.CDMA_PHONE
* and TelephonyManager.PHONE_TYPE_GSM if RILConstants.GSM_PHONE
*/
int getActivePhoneType();
/**
* Returns the CDMA ERI icon index to display
*/
int getCdmaEriIconIndex();
/**
* Returns the CDMA ERI icon mode,
* 0 - ON
* 1 - FLASHING
*/
int getCdmaEriIconMode();
/**
* Returns the CDMA ERI text,
*/
String getCdmaEriText();
/**
* Returns true if CDMA provisioning needs to run.
*/
boolean getCdmaNeedsProvisioning();
/**
* Returns the unread count of voicemails
*/
int getVoiceMessageCount();
/**
* Returns the network type
*/
int getNetworkType();
/**
* Return true if an ICC card is present
*/
boolean hasIccCard();
}
package zy.phone;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import android.telephony.TelephonyManager;
import android.util.Log;
public class PhoneUtils {
/**
* 从TelephonyManager中实例化ITelephony,并返回
*/
static public com.android.internal.telephony.ITelephony getITelephony(TelephonyManager telMgr) throws Exception {
Method getITelephonyMethod = telMgr.getClass().getDeclaredMethod("getITelephony");
getITelephonyMethod.setAccessible(true);//私有化函数也能使用
return (com.android.internal.telephony.ITelephony)getITelephonyMethod.invoke(telMgr);
}
static public void printAllInform(Class clsShow) {
try {
// 取得所有方法
Method[] hideMethod = clsShow.getDeclaredMethods();
int i = 0;
for (; i < hideMethod.length; i++) {
Log.e("method name", hideMethod[i].getName());
}
// 取得所有常量
Field[] allFields = clsShow.getFields();
for (i = 0; i < allFields.length; i++) {
Log.e("Field name", allFields[i].getName());
}
} catch (SecurityException e) {
// throw new RuntimeException(e.getMessage());
e.printStackTrace();
} catch (IllegalArgumentException e) {
// throw new RuntimeException(e.getMessage());
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
参考: http://lpqsun-126-com.iteye.com/blog/1170911
http://hejianjie.iteye.com/blog/136205