Fork me on GitHub
返回顶部
跳到底部

反射

Java反射机制是指在运行状态中,对于任意一个类,都能够知道这个类所有的属性和方法;对于任何一个对象,都能够调用它的任意一个方法和属性。Java反射机制为Java本身带来了动态性。(反射提供了一种运行期获取对象元信息的手段,通过反射,我们可以在运行时获得程序中每一个类的成员和成员的信息)

获取Class对象

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

当我们得到一个class对象后,如果我们不知道该class对象属于哪个类的class对象,我们可也用如下方式判断:

public native boolean isInstance(Object obj);

下面是一个使用例子,来判断clazz是不是String类的class对象:

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对象,通过它可以得到指定的方法的各种信息。

通过反射调用调用方法

获取一个类的指定方法信息后,就可以用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]

反射与泛型

泛型化的Class

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

public final class Class<T>

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

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

上面获得String类的class对象后,没有带上泛型信息,然后我们用其创建实例时就需要强制类型转换。我们可以加上泛型将以上情况改善:

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

其中getClass和newInstance方法的原型如下。
Object类里的getClass()方法:

public final native Class<?> getClass();

Class类的原型及其里面的newInstance()方法:

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;
}

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

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   
posted @ 2018-05-14 16:25  sqmax  阅读(227)  评论(0编辑  收藏  举报