反射是程序在运行状态下,动态的获取某个类的内部信息的一种操作。例如:类名,包名,所有属性的集合,所有方法的集合,构造方法的集合等。该操作发生在程序的运行时状态,所以编译器管不着有关反射的一些代码,通常只有在运行时才能暴露出程序的内部错误。反射的核心在于'Class'这个类,本篇将从 Class 这个类开始介绍有关反射的一些基本的概念,主要内容如下:
一、获取 Class 对象 此处的 Class 是一个具体的类(java.lang.Class),并不是我们自定义一个类时所使用的关键字 class。这是一个泛型类,通常有两种方法可以获取该对象。第一种方式,使用类名. class 来获取 Class 对象。
- Class<Integer> integerClass = int.class;
- Class<Double> doubleClass = double.class;
- Class<Character> characterClass = char.class;
- .......
- Class<Map> mapClass = Map.class;
- Class<String> stringClass = String.class;
- Class<Date> dateClass = Date.class;
- ........
无论是基本数据类型,还是一般的 class 类型,或是接口类型,都是可以通过. class 的方式来获取与之对应的 Class 对象。第二中获取 Class 对象的方法是,通过 getClass 方法,当然不是所有类都提供了这个方法的,例如:Object 类就提供这么一个方法:
- public final native Class<?> getClass();
还有我们的数组类型:
- int[] array = new int[23];
- Class<?> arrayClass = array.getClass();
鉴于不是所有类都提供了这个方法,一般建议使用第一种方式获取 Class 对象。
下面看看有关我们获取到的这个 Class 对象的一些基本信息。首先我们可以获取有关该 Class 对象的名称,包,父类,父接口等信息。
- //获取该Class对象代表的类名
- public String getName()
- //获取该Class对象代表的类名,不包含包名
- public String getSimpleName()
- //获取该Class对象所代表的类的包名
- public Package getPackage()
- //获取该类继承的父类对象
- public native Class<? super T> getSuperclass();
- //获取该类实现的所有接口的集合
- public Class<?>[] getInterfaces()
- //获取修饰在该类上的访问修饰符
- public native int getModifiers();
- //获取该类中所有内部类的集合
- public Class<?>[] getClasses()
- ............
还有一些有关注解的信息,由于之前在介绍注解的时候已经说明过,此处不再赘述。上面的这些方法的使用还是比较简单,此处不再演示。
二、获取 Class 对象中字段信息 类中的字段包括实例域和静态域。在 Java 反射机制中,使用 Field 类管理字段信息。
- //获取该类中所有字段的集合(public修饰符修饰的)
- public Field[] getFields()
- //返回指定名称的字段(public修饰符修饰的)
- public Field getField(String name)
- //获取所有字段的集合,不包括从父类继承的(包括非public)
- public Field[] getDeclaredFields()
- //获取指定的字段,不包含从父类继承的(可以是非public)
- public Field getDeclaredField(String name)
对以上四种获取字段 Field 的方法做一点说明,getFields 或者 getField 方法可以看见该类所有的被 public 修饰的字段,包括从父类继承的,但是不可见非 public 修饰的字段。getDeclaredFields 或者 getDeclaredField 方法可以看见该类所有的字段,包括非 public 修饰的,但是不可见父类中的字段。接下来,我们看看对获取到的 field 字段可以做哪些操作:
- //获取该字段的名称
- public String getName()
- //获取该字段的访问修饰符
- public int getModifiers()
- //获取指定对象中的该字段的值
- public Object get(Object obj)
- //设置指定对象中该字段的值
- public void set(Object obj, Object value)
- //获取该字段的类型
- public Class<?> getType()
- //判断当前程序是否具有访问该字段的权限
- public boolean isAccessible()
- //flag设为true表示忽略Java的访问检查机制,以允许读写非public的字段
- public void setAccessible(boolean flag)
- .......
对于以上的几个基本的方法,可以说见名知意,这里需要说明的是 get 和 set 方法,先看个例子:
- Class<Student> studentClass = Student.class;
- //这里的school是Student类中的一个属性
- Field f = studentClass .getDeclaredField("school");
- Student s = new Student();
- f.set(s,"yanSchool");
- System.out.println(f.get(s));
- 输出结果:yanSchool
这里的 f 代表了 school 这个实例属性,如果别的类中有相同的属性,我们是可以通过 f 为该属性赋值的,当然也可以从某个具有该属性的类中获取该属性的值,前提是具备目标类中的该属性的访问权限。上述的 getModifiers 返回的是 int 值,该值代表了一个修饰符,想要转换成我们能看懂的字符串的形式需要使用 Modifier.toString(int a) 方法,将刚刚返回的 int 值作为参数传入即可。
三、获取 Class 对象的方法信息 无论是静态方法还是实例方法,在 Java 反射机制中都是使用 Method 这个类来管理的,一个方法对应于一个 Method 对象。先看怎么通过 Class 对象获取其中的方法。
- //返回所有被public修饰的方法的集合,包括父类的
- public Method[] getMethods()
- //返回指定的被public修饰的方法,包括父类中的
- public Method getMethod(String name, Class<?>... parameterTypes)
- //返回所有的方法的集合(可以有非public),不包括父类的
- public Method[] getDeclaredMethods()
- //返回本类中声明的指定名称和参数类型的方法
- public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
对于以上四个方法需要说明一点的是,getMethod 或者 getDeclaredMethod 在获取指定方法的时候,需要传入该方法的形参类型,如果没有传入或者传入错误就会抛出 NoSuchMethodException 异常。因为方法名和方法参数类型(个数)是唯一确定一个方法的凭据。对于获取到的 Method 方法有以下一些操作:
- //获取该方法的方法名
- public String getName()
- public int getModifiers()
- //获取该方法的返回类型
- public Class<?> getReturnType()
- //获取所有参数的类型
- public Class<?>[] getParameterTypes()
- //获取方法声明抛出的异常类型
- public Class<?>[] getExceptionTypes()
- //将该方法在指定的对象上执行,有形参的需要传入形参值
- public Object invoke(Object obj, Object... args)
- ....//还有一些有关注解的操作,此处不再赘述
上述方法中的 invoke 方法和之前介绍的字段的 get/set 方法一样,需要指定目的对象才能使用,因为他们只是代表了具体的一个字段或者一个方法。
- public class Student extends People {
- public String school;
- private int sno;
- public void sayHello(String str){
- System.out.println("hello "+str);
- }
- public void sayHello(int s){
- System.out.println("my age is "+s);
- }
- }
- public static void main(String[] args){
- Class<Student> studentClass = Student.class;
- Method method = studentClass.getMethod("sayHello",String.class);
- method.invoke(new Student(),"walker");
- }
- 输出结果:hello walker
四、获取构造方法和创建实例对象 一旦我们获取到了 Class 对象,我们就可以通过以下的方法进一步获取其中的构造方法,并使用它们创建一个实例对象。
- //获取所有被public修饰的构造方法,不包含父类中的
- public Constructor<?>[] getConstructors()
- //获取指定的被pubic修饰的构造方法,不包含父类中的
- public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
- //返回所有的构造方法,可以包含非public修饰的
- public Constructor<?>[] getDeclaredConstructors()
- //返回指定的构造方法,可以是非public修饰的
- public Constructor<T> getConstructor(Class<?>... parameterTypes)
以上的方法和之前介绍的字段 Field,方法 Method 很是相似。下面我们看看如何使用 Constructor 做一些事情。
- public String getName()
- public int getModifiers()
- public Class<?>[] getParameterTypes()
- //创建该类的实例对象
- public T newInstance(Object ... initargs)
- ........一些注解有关的信息,可以查看之前的文章
我们主要看看如何通过 newInstance 创建一个实例对象:
- public class Student extends People {
- public String school;
- publicint sno;
- public Student(String school,int sno){
- this.school=school;
- this.sno = sno;
- }
- public void sayHello(String str){
- System.out.println("hello "+str);
- }
- public void sayHello(int s){
- System.out.println("my age is "+s);
- }
- }
- public static void main(String[] args){
- Class<Student> studentClass = Student.class;
- Constructor constructor = studentClass.getConstructor(String.class,int.class);
- Student s = (Student) constructor.newInstance("walker", 21);
- System.out.println(s.school+" "+s.sno);
- }
- 输出结果:walker 21
五、反射机制的一些细节 前面我们一直讨论的都是具体的类,我们可以从这些类中获取到字段,方法,构造器,注解等。java.lang.reflect 包中对数组类型增添了专门的类 Array 来实现反射,这里的 Array 和数组中的 Arrays 是不同的。
- //创建指定元素类型、指定长度的数组,
- public static Object newInstance(Class<?> componentType, int length)
- //创建多维数组
- public static Object newInstance(Class<?> componentType, int... dimensions)
- //获取数组array指定的索引位置index处的值
- public static native Object get(Object array, int index)
- //修改数组array指定的索引位置index处的值为value
- public static native void set(Object array, int index, Object value)
- //返回数组的长度
- public static native int getLength(Object array)
我们可以通过 Array 类在运行时动态创建数组和操作数组中的元素,而不必想之前一样必须在编译之前完成数组的创建。需要注意的是此处返回的数组类型是 Object 而非 Object[],那时因为前者可以转化成具体类型的数组,后者则不能。
至此,反射的基本内容介绍完了,我们应该知道,虽然反射很是灵活,可以动态的读取类的信息,动态的创建实例对象和数组等,但是没有了编译器的一层检查,很容易导致运行是异常。如果能够用接口来完成的,尽量使用接口来完成。
理解不到之处,还望大家不吝赐教!
来源: http://www.cnblogs.com/yangming1996/p/6790478.html