Skip to content
sqmax edited this page May 13, 2018 · 1 revision

反射

标签(空格分隔): javaSE复习


反射提供了一种运行期获取对象元信息的手段,通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。

获取Class对象

  • 使用Class类的forName静态方法:
  • 直接根据该类获取(.class)。
  • 调用该类对象的getClass方法,该方法继承自Object对象。

当我们用第一种方式种方式获得一个class对象后,如:Class cls=Class.forName(str),我们不知道该Class对象属于哪个类的Class对象,我们可也用如下方式判断:

public native boolean isInstance(Object obj);

下面是一个使用例子:

Class cls=Class.forName("java.lang.String");
boolean flag=clazz.isInstance(new String ());

获取类的各种信息

获取到一个类的Class对象后,可以获取类的各种信息,进行很多操作。 如:

创建实例
获取方法信息
获取构造器信息
获取类的成员变量信息
利用反射创建数组 ......

可以查看Class类的源代码,里面的方法都是用来获取一个类的详细信息的。

通过反射创建一个类的实例

通过反射创建对象主要有两种方式。
(1)使用Class对象的newInstance()方法来创建Class对象对应的实例。

Class clazz=String.class;
String str1=(String) clazz.newInstance();

(2)先通过Class对象获取指定的Constructor对象,在调用Constructor对象的newInstance()方法来创建实例。

Class clazz=String.class;
Constructor constructor=clazz.getConstructor(String.class);
constructor.newInstance("hello");

这种方式的好处是可以使用指定的构造器来创建实例。

获取方法信息

获取该类的public方法:

public Method[] getMethods() throws SecurityException

获取该类的所有方法:

public Method[] getDeclaredMethods() throws SecurityException

获取指定方法:

public Method getMethod(String name, Class<?>... parameterTypes)

该方法第一个参数以要获取的方法名,后面一个可变数组是要获取的方法的参数。

通过反射调用调用方法

获取一个类的指定方法信息后,就可以用Method实例的invoke方法调用该指定方法。
invoke方法原型如下:

public Object invoke(Object obj, Object... args)

使用方式如下:

public class Hello {
    public int add(int a,int b){
        return a+b;
    }
    public static void main(String[] args) throws Exception {
        Class clazz = Hello.class;
        Hello hello=(Hello)clazz.newInstance();

        Method method = (Method) clazz.getMethod("add", int.class, int.class);
        Object result=method.invoke(hello,1,2);
        System.out.println(result);
    }
}

以上就是使用反射的方式来执行一个方法。

利用反射创建数组

在Java中,数组也是一种特殊的引用类型,它也可以赋值给一个Object应用。利用反射创建数组主要要到Array类。
下面我们看一下利用反射创建数组的例子:

//        创建长度为10的String类型的一维数组
        Object array= Array.newInstance(String.class,10);
        String[] strArray=(String[]) array;
//        向数组添加元素
        Array.set(array,0,"Java");
        Array.set(array,1,"C++");
        Array.set(array,2,"PHP");
        System.out.println(Arrays.asList(strArray));

输出结果如下:

[Java, C++, PHP, null, null, null, null, null, null, null]

反射与泛型

从JDK5以后,Java的Class类增加了泛型的功能,从而允许使用泛型来限制Class类。通过在反射中使用泛型,可以避免使用反射生成的对象需要强制转换。 Class类的定义如下:

public final class Class<T>

所以我们通过某种方式获得一个类的Class对象后,不要忘记带让其泛型信息。下面例子可以简单地说明:

Class cls1=new String().getClass();
String str1=(String)cls1.newInstance();

我们可以加上泛型将以上情况改善。

Class<? extends String> cls2=new String().getClass();
String str2=cls2.newInstance();

其中getClass和newInstance方法的原型如下:

public final native Class<?> getClass();
public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {
....
public T newInstance()
        throws InstantiationException, IllegalAccessException
{
    .....
}
....

使用反射获取泛型信息

通过反射获得成员变量的Field对象后,就可以很容易地获得改成员变量的数据类型。如下:
首先,有一个用来测试的类:

class MyField{
    public String str;
    public Map<String,Object> map;
}

我们来获取成员变量的信息:

Class<MyField> cls=MyField.class;
Field strField=cls.getField("str");
Class<?> clsF=strField.getType();
System.out.println(clsF);

输出:

class java.lang.String

但这种方式只使用对成员变量含有泛型的类型,会丢失成员变量的泛型信息,如:Map<String,Object> map
我们可以用另一种方式获取带有泛型的成员变量的信息:
Type clsM=mapField.getGenericType(); 然后将Type对象强制转换成ParameterizedType对象,它代表被参数化的类型,也就是增加了泛型限制的类型。
它提供了如下两个方法:

  • getRawType():返回没有泛型参数的原始类型。
  • getActualTypeArguments():返回泛型参数的类型。
Class<MyField> cls=MyField.class;

Field mapField=cls.getField("map");
Type type=mapField.getGenericType();
ParameterizedType parameterizedType=(ParameterizedType)type;
      
Type rType=parameterizedType.getRawType();
System.out.println("原始类型:"+rType);
Type[] typeArguments=parameterizedType.getActualTypeArguments();
for(int i=0;i<typeArguments.length;i++){
    System.out.println("第"+i+"个泛型参数是:"+typeArguments[i]);
}

输出结果如下:

原始类型:interface java.util.Map
第0个泛型参数是:class java.lang.String
第1个泛型参数是:class java.lang.Object