反射基础
1、使用反射的启点总是 java.lang.Class
实例
Class
实例的简便快捷方式:
try{
Class<?> class = Class.forName(name);//此方法会完成类的初始化工作(执行静态块)
}catch(ClassNotFoundException e){
e.printStackTrace();
}
//如果已经装入了类,您将得到现有的 Class 信息。如果类未被装入,类装入器将现在装入并返回新创建的类实例。
2、得到Class 对象后,可以获得类自身的信息,如:包和父类、及实现的接口、定义的构造函数和属性、方法等信息。
Constructor<T> | getConstructor(Class<?>... parameterTypes) 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。 |
Constructor
对象,它反映此
Class
对象所表示的类的指定公共构造方法。
parameterTypes
参数是
Class
对象的一个数组,这些 Class 对象按声明顺序标识构造方法的形参类型。
Constructor<?>[] | getConstructors() 返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。 |
Constructor<T> | getDeclaredConstructor(Class<?>... parameterTypes) 返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法 |
Constructor<?>[] | getDeclaredConstructors() 返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。 |
java.lang.reflect.Constructor
函数。这种
Constructor
类定义
ne wInstance
方法,它采用一组对象作为其唯一的参数,然后返回新创建的原始类实例。
3、得到构造函数后可以利用 java.lang.reflect.Constructor类 函数创建类的实例对象
T | newInstance(Object... initargs) 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。 |
java编程语言还定义了一种您可以用来使用 无参数(或缺省)构造函数创建类的一个实例的特殊快捷方式。这种快捷方式嵌入到 Class
定义中,如下:
Object newInstance()
-- 使用缺省函数创建新的实例
4、获得Field (类包含的属性)
-
Field getField(String name)
-- 获得命名的公共字段 -
Field[] getFields()
-- 获得类的所有公共字段 -
Field getDeclaredField(String name)
-- 获得类声明的命名的字段 -
Field[] getDeclaredFields()
-- 获得类声明的所有字段
getXXX
和
setXXX
方法。
| getAnnotation(Class<T> annotationClass) 如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。 |
5、获得Method
-
Method getMethod(String name, Class[] params)
-- 使用特定的参数类型,获得命名的公共方法 -
Method[] getMethods()
-- 获得类的所有公共方法Method getDeclaredMethod(String name, Class[] params)
-- 使用特写的参 数类型,获得类声明的命名的方法-
Method[] getDeclaredMethods()
-- 获得类声明的所有方法
invoke
方法使用两个参数,为调用提供类实例和参数值数组。
-
- 对带有指定参数的指定对象调用由此
Method
对象表示的底层方法。个别参数被自动解包,以便与基本形参相匹配,基本参数和引用参数都随需服从方法调用转换。如果底层方法是静态的,那么可以忽略指定的
obj
参数。该参数可以为 null。如果底层方法所需的形参数为 0,则所提供的
args
数组长度可以为 0 或 null。如果底层方法是实例方法,则使用动态方法查找来调用它。
案例:@Test public void testClassForname(){ try { Class<?> callClass = Class.forName("com.lucene.Student"); Method method= callClass.getMethod("setName", new Class[]{String.class}); Method method2= callClass.getDeclaredMethod( "getName", null );//得到无参的getName()方法 System. out.println(method.getName()); Object object = callClass.newInstance(); method.invoke(object, new String[]{"你好"});//调用object对象的指定方法,并传入 参数值(String数组) System. out.println( method2.invoke(object, null)); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }
- 对带有指定参数的指定对象调用由此
反射数组
数组是Java编程语言中的对象。与所有对象一样,它们都有类。如果您有一个数组,使用标准 getClass
方法,您可以获得该数组的类,就象任何其它对象一样。但是,即使您有一个数组类,您也不能直接对它进行太多的操作 -- 反射为标准类提供的构造函数接入不能用于数组,而且数组没有任何可接入的字段,只有基本的 java.lang.Object
方法定义用于数组对象。
数组的特殊处理使用 java.lang.reflect.Array
类提供的静态方法的集合。该类中的方法使您能够创建新数组,获得数组对象的长度,读和写数组对象的索引值。
清单5显示了一种重新调整现有数组大小的有效方法。它使用反射来创建相同类型的新数组,然后在返回新数组之前,在老数组中复制所有数据。
清单 5:通过反射来扩展一个数组
public Object growArray(Object array, int size) {
Class type = array.getClass().getComponentType();
Object grown = Array.newInstance(type, size);
System.arraycopy(array, 0, grown, 0,
Math.min(Array.getLength(array), size));
return grown;
}
Class<?> | getComponentType() 返回表示数组组件类型的 Class 。 |
staticObject | newInstance(Class<?> componentType, int... dimensions) 创建一个具有指定的组件类型和维度的新数组。 |
安全性和反射
在处理反射时安全性是一个较复杂的问题。反射经常由框架型代码使用,由于这一点,您可能希望框架能够全面接入您的代码,无需考虑常规的接入限制。但是,在其它情况下,不受控制的接入会带来严重的安全性风险,如当代码在不值得信任的代码共享的环境中运行时。
由于这些互相矛盾的需求,Java编程语言定义一种多级别方法来处理反射的安全性。基本模式是对反射实施与应用于源代码接入相同的的限制:
- 从任意位置到类公共组件的接入
- 类自身外部无任何到私有组件的接入
- 受保护和打包(缺省接入)组件的有限接入
不过-至少某些时候,围绕这些限制有一种简单的方法。我在前面实例中使用的 Constructor
、 Field
和 Method
类都扩展了一个普通的基本类--  java.lang.reflect.AccessibleObject
类。该类定义一种 setAccessible
方法,使您能够启动或关闭对这些类中其中一个类的实例的接入检测。唯一的问题在于如果使用了安全性管理器,它将检测正在关闭接入检测的代码是否许可了这样做。如果未许可,安全性管理器抛出一个例外。
清单6展示了一个程序,在 清单 1TwoString
类的一个实例上使用反射来显示安全性正在运行:
清单 6:反射安全性正在运行
public class ReflectSecurity {
public static void main(String[] args) {
try {
TwoString ts = new TwoString("a", "b");
Field field = clas.getDeclaredField("m_s1");
// field.setAccessible(true);
System.out.println("Retrieved value is " +
field.get(inst));
} catch (Exception ex) {
ex.printStackTrace(System.out);
}
}
}
如果您编译了这一程序,不使用任何特定参数直接从命令行运行,它将在 field.get(inst)
调用中抛出一个 IllegalAccessException
。如果您未注释 field.setAccessible(true)
代码行,那么重新编译并重新运行该代码,它将取得成功。最后,如果您在命令行添加了JVM参数 -Djava.security.manager
以实现安全性管理器,它将再次失败,除非您定义了 ReflectSecurity
类的许可权限。