Java反射:
1. Java反射机制可以让我们在编译期(Compile Time)之外的运行期(Runtime)检查类,接口,变量以及方法的信息。反射还可以让我们在运行期实例化对象,调用方法,通过调用get/set方法
获取变量的值。
2. 下面是一个Java反射的简单例子:
Method[] methods = MyObject.class.getMethods(); for(Method method : methods){ System.out.println("method = " + method.getName()); }
Classes:
1. 获取Class对象:
1). Class myObjectClass = MyObject.class;
2). String className = ... ;//在运行期获取的类名字符串
Class class = Class.forName(className);
在使用Class.forName()方法时,你必须提供一个类的全名,这个全名包括类所在的包的名字。例如MyObject类位于com.jenkov.myapp包,那么他的全名就是com.jenkov.myapp.MyObject。
3). myObject.getClass()
2. 获取类名:
1).通过getName() 方法返回类的全限定类名(包含包名)
2).如果你仅仅只是想获取类的名字(不包含包名),那么你可以使用getSimpleName()方法:
3. 获取修饰符:
可以通过Class对象来访问一个类的修饰符,即public,private,static等等的关键字:
int modifiers = aClass.getModifiers();
修饰符都被包装成一个int类型的数字,这样每个修饰符都是一个位标识(flag bit),这个位标识可以设置和清除修饰符的类型。可以使用java.lang.reflect.Modifier类中的方法来检查修饰符的类型:
Modifier.isAbstract(int modifiers); Modifier.isFinal(int modifiers); Modifier.isInterface(int modifiers); Modifier.isNative(int modifiers); Modifier.isPrivate(int modifiers); Modifier.isProtected(int modifiers); Modifier.isPublic(int modifiers); Modifier.isStatic(int modifiers); Modifier.isStrict(int modifiers); Modifier.isSynchronized(int modifiers); Modifier.isTransient(int modifiers); Modifier.isVolatile(int modifiers);
4. 获取包信息:
Package package = aClass.getPackage();
通过Package对象你可以获取包的相关信息,比如包名
5. 获取父类:
Class superclass = aClass.getSuperclass();
可以看到superclass对象其实就是一个Class类的实例,所以你可以继续在这个对象上进行反射操作。
6. 获取实现的接口:
Class[] interfaces = aClass.getInterfaces();
由于一个类可以实现多个接口,因此getInterfaces();方法返回一个Class数组,在Java中接口同样有对应的Class对象。
注意:getInterfaces()方法仅仅只返回当前类所实现的接口。当前类的父类如果实现了接口,这些接口是不会在返回的Class集合中的,尽管实际上当前类其实已经实现了父类接口。
构造器:
1. 利用Java的反射机制你可以检查一个类的构造方法,并且可以在运行期创建一个对象。这些功能都是通过java.lang.reflect.Constructor这个类实现的。
2. 获取Constructor对象:
1).无参构造器:Constructor[] constructors = aClass.getConstructors();
2).有参构造器:Constructor constructor = aClass.getConstructor(new Class[]{String.class});
如果没有指定的构造方法能满足匹配的方法参数则会抛出:NoSuchMethodException。
3. 获取构造方法参数:
Class[] parameterTypes = constructor.getParameterTypes();
4. 利用Constructor对象实例化一个类:
Constructor constructor = MyObject.class.getConstructor(String.class);
MyObject myObject = (MyObject)constructor.newInstance("constructor-arg1");
constructor.newInstance()方法的方法参数是一个可变参数列表,但是当你调用构造方法的时候你必须提供精确的参数,即形参与实参必须一一对应。在这个例子中构造方法需要
一个String类型的参数,那我们在调用newInstance方法的时候就必须传入一个String类型的参数。
变量:
1. 使用Java反射机制你可以运行期检查一个类的变量信息(成员变量)或者获取或者设置变量的值。
2. 获取Field对象:
1).获取声明为公有的(public)的所有变量集合:Field[] methods = aClass.getFields();
2).获取已知变量名称的变量:Field field = aClass.getField("someField");
在调用getField()方法时,如果根据给定的方法参数没有找到对应的变量,那么就会抛出NoSuchFieldException。
3. 获取变量名称:
String fieldName = field.getName();
4. 获取变量类型:
Object fieldType = field.getType();
5. 获取或设置(get/set)变量值:
Class aClass = MyObject.class Field field = aClass.getField("someField"); MyObject objectInstance = new MyObject(); Object value = field.get(objectInstance); field.set(objetInstance, value);
传入Field.get()/Field.set()方法的参数objetInstance应该是拥有指定变量的类的实例。在上述的例子中传入的参数是MyObject类的实例,是因为someField是MyObject类的实例。
如果变量是静态变量的话(public static)那么在调用Field.get()/Field.set()方法的时候传入null做为参数而不用传递拥有该变量的类的实例。(译者注:你如果传入拥有该变量
的类的实例也可以得到相同的结果)
方法
1. 使用Java反射你可以在运行期检查一个方法的信息以及在运行期调用这个方法,通过使用java.lang.reflect.Method类就可以实现上述功能。
2. 获取Method对象:
1).获取声明为公有的(public)的所有变量集合:Method[] methods = aClass.getMethods();
2).根据方法名和参数类型获取指定方法:Method method = aClass.getMethod("doSomething", new Class[]{String.class});
如果根据给定的方法名称以及参数类型无法匹配到相应的方法,则会抛出NoSuchMethodException。
3).如果你想要获取的方法没有参数,那么在调用getMethod()方法时第二个参数传入null即可,就像这样:
Method method = aClass.getMethod("doSomething", null);
3. 获取方法参数以及返回类型:
1).获取指定方法的方法参数:Class[] parameterTypes = method.getParameterTypes();
2).获取指定方法的返回类型:Class returnType = method.getReturnType();
4. 通过Method对象调用方法:
//获取一个方法名为doSomesthing,参数类型为String的方法 Method method = MyObject.class.getMethod("doSomething", String.class); Object returnValue = method.invoke(null, "parameter-value1");
传入的null参数是你要调用方法的对象,如果是一个静态方法调用的话则可以用null代替指定对象作为invoke()的参数,在上面这个例子中,如果doSomething不是静态方法的话,
你就要传入有效的MyObject实例而不是null。Method.invoke(Object target, Object … parameters)方法的第二个参数是一个可变参数列表,但是你必须要传入与你要调用方法
的形参一一对应的实参。就像上个例子那样,方法需要String类型的参数,那我们必须要传入一个字符串。
私有变量和私有方法
1. 在通常的观点中从对象的外部访问私有变量以及方法是不允许的,但是Java反射机制可以做到这一点。使用这个功能并不困难,在进行单元测试时这个功能非常有效。
2. 访问私有变量:
要想获取私有变量你可以调用Class.getDeclaredField(String name)方法或者Class.getDeclaredFields()方法。Class.getField(String name)和Class.getFields()只会返回公有
的变量,无法获取私有变量。下面例子定义了一个包含私有变量的类,在它下面是如何通过反射获取私有变量的例子:
Field privateStringField = PrivateObject.class.getDeclaredField("privateString"); privateStringField.setAccessible(true); String fieldValue = (String) privateStringField.get(privateObject);
注意调用PrivateObject.class.getDeclaredField(“privateString”)方法会返回一个私有变量,这个方法返回的变量是定义在PrivateObject类中的而不是在它的父类中定义的变量。
注意privateStringField.setAccessible(true)这行代码,通过调用setAccessible()方法会关闭指定类Field实例的反射访问检查,这行代码执行之后不论是私有的、受保护的以及包访问
的作用域,你都可以在任何地方访问,即使你不在他的访问权限作用域之内。但是你如果你用一般代码来访问这些不在你权限作用域之内的代码依然是不可以的,在编译的时候就会报错。
3. 访问私有方法:
访问一个私有方法你需要调用 Class.getDeclaredMethod(String name, Class[] parameterTypes)或者Class.getDeclaredMethods() 方法。
Class.getMethod(String name, Class[] parameterTypes)和Class.getMethods()方法,只会返回公有的方法,无法获取私有方法。下面例子定义了一个包含私有方法的类,在它下面
是如何通过反射获取私有方法的例子:
Method privateStringMethod = PrivateObject.class.getDeclaredMethod("getPrivateString", null); privateStringMethod.setAccessible(true); String returnValue = (String)privateStringMethod.invoke(privateObject, null);
PrivateObject.class.getDeclaredMethod(“privateString”)方法会返回一个私有方法,这个方法是定义在PrivateObject类中的而不是在它的父类中定义的。
同样的,注意Method.setAcessible(true)这行代码,通过调用setAccessible()方法会关闭指定类的Method实例的反射访问检查,这行代码执行之后不论是私有的、受保护的以及包访问
的作用域,你都可以在任何地方访问,即使你不在他的访问权限作用域之内。但是你如果你用一般代码来访问这些不在你权限作用域之内的代码依然是不可以的,在编译的时候就会报错。