反射
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