所谓反射,能够理解为在运行时期获取对象类型信息的操做。传统的编程方法要求程序员在编译阶段决定使用的类型,可是在反射的帮助下,编程人员能够动态获取这些信息,从而编写更加具备可移植性的代码。严格地说,反射并不是编程语言的特性,由于在任何一种语言均可以实现反射机制,可是若是编程语言自己支持反射,那么反射的实现就会方便不少。
java
1,得到类型类
咱们知道在Java中一切都是对象,咱们通常所使用的对象都直接或间接继承自Object类。Object类中包含一个方法名叫getClass,利用这个方法就能够得到一个实例的类型类。类型类指的是表明一个类型的类,由于一切皆是对象,类型也不例外,在Java使用类型类来表示一个类型。全部的类型类都是Class类的实例。例如,有以下一段代码:
A a = new A();
if(a.getClass()==A.class)
System.out.println("equal");
else System.out.println("unequal");程序员
能够看到,对象a是A的一个实例,A某一个类,在if语句中使用a.getClass()返回的结果正是A的类型类,在Java中表示一个特定类型的类型类能够用“类型.class”的方式得到,由于a.getClass()得到是A的类型类,也就是A.class,所以上面的代码执行的结果就是打印出“equal”。特别注意的是,类型类是一一对应的,父类的类型类和子类的类型类是不一样的,所以,假设A是B的子类,那么以下的代码将获得“unequal”的输出:
A a = new A();
if(a.getClass()==B.class)
System.out.println("equal");
else System.out.println("unequal");编程
所以,若是你知道一个实例,那么你能够经过实例的“getClass()”方法得到该对象的类型类,若是你知道一个类型,那么你可使用“.class”的方法得到该类型的类型类。
2,得到类型的信息
在得到类型类以后,你就能够调用其中的一些方法得到类型的信息了,主要的方法有:
getName():String:得到该类型的全称名称。
getSuperClass():Class:得到该类型的直接父类,若是该类型没有直接父类,那么返回null。
getInterfaces():Class[]:得到该类型实现的全部接口。
isArray():boolean:判断该类型是不是数组。
isEnum():boolean:判断该类型是不是枚举类型。
isInterface():boolean:判断该类型是不是接口。
isPrimitive():boolean:判断该类型是不是基本类型,便是否是int,boolean,double等等。
isAssignableFrom(Class cls):boolean:判断这个类型是不是类型cls的父(祖先)类或父(祖先)接口。
getComponentType():Class:若是该类型是一个数组,那么返回该数组的组件类型。
此外还能够进行类型转换这类的操做,主要方法有:
asSubclass(Class clazz):Class:将这个类型转换至clazz,若是能够转换,那么老是返回clazz这个引用,不然抛出异常。
cast(Object obj):Object:将obj强制转换为这个类型类表明的类型,不能转换的话将抛出异常。数组
除了这些之外,利用类型类还能够反射该类型中的全部属性和方法。在Java中全部的属性信息都用Field表示,全部的方法信息都用Method表示,这辆各种都是java.lang.reflect包中的类。在Class中提供了4个相关的方法得到类型的属性:
getField(String name):Field
getFields():Field[]
getDeclaredField(String name):Field
getDeclaredFields():Field[]
其中getField用于返回一个指定名称的属性,可是这个属性必须是公有的,这个属性能够在父类中定义。若是是私有属性或者是保护属性,那么都会抛出异常提示找不到这个属性。getFields则是返回类型中的全部公有属性,全部的私有属性和保护属性都找不到。getDeclaredField得到在这个类型的声明中定义的指定名称的属性,这个属性必须是在这个类型的声明中定义,但可使私有和保护的。getDeclaredFields得到在这个类型的声明中定义的全部属性,包括私有和保护的属性都会被返回,可是全部父类的属性都不会被返回。举个例子,先考虑下面两个类的声明:
class A extends B {
public int a1;
private int a2;
}
class B {
public int b1;
private int b2;
}
若是利用A的类型类调用getFields,那么会返回a1和b1两个属性,若是调用getField("a2")则会报错;若是调用getDeclaredFields则会返回a1和a2,若是调用getDeclaredField("b1")则会报错。架构
对于方法也有相似的函数即:
getMethods():Method[]
getMethod(String name, Class ... parameterTypes):Method
getDeclaredMethods():Method[]
getDeclaredMethod(Strubg name, Class ...parameterTypes):Method
不定长参数...是JDK5.0之后新加入的语法。这几个方法的用法和上面的相似,只是在得到特定方法时,除了要告知方法的名字,还须要告知方法的参数,若是没有参数,那么能够传递null,或者空数组,可是最好的方法就是什么都不写,编译器会自行解决不定长参数问题。
若是要得到全部的属性(方法),包括公有和私有的,那么就必须利用getDeclareFields(getDeclareMethods)方法,而后再利用getSuperClass的方法得到父类,而后递归下去。
3,属性和方法
全部的属性都使用Field表示,全部的方法都使用Method表示。利用Field和Method能够得到属性和方法的信息,甚至执行是获取、修改属性值和调用方法。
对于属性,主要有如下方法可使用:
getType():Class:得到该属性的类型。
getName():String:得到属性名称。
isAccessible():boolean:判断该属性是不是能够访问的,一般私有和保护的类型都是不能够访问的。
get(Object obj):Object:得到实例obj的属性值,若是该实例的类型中不包含这个属性,那么就会报错。
set(Object obj, Object value):设置该实例的属性值
setAccessible(boolean flag):设置该属性是否能够访问,若是你调用get和set方法,那么有可能会引起访问权限的错误,这个时候你能够调用setAccessible方法使得该属性能够访问。例以下面的代码:
A a = new A();
Field f = A.class.getDeclaredField("a2");
f.setAccessibe(true);
System.out.println(f.get(a));
f.set(a,12);
System.out.println(f.get(a));
若是移出中间的f.setAccessibe(true);那么代码会报错,反之输出0 12。
对于属性而言,若是该属性的类型是基本类型,那么还可使用一些便捷的set和get操做,例如getInt,setInt什么的,你能够根据本身的须要调用相应的方法。
对于方法,能够有如下的方法:
getName():String:得到方法的名字。
getReturnType():Class:得到方法的返回值类型。
getParameterTypes():Class[]:得到方法的参数类型。
isAccessible():boolean:判断该方法是不是能够访问的。
setAccessible(boolean flag):设置该方法是否能够访问。
invoke(Object obj, Object... args):Object:调用实例obj的相应方法,其参数由args给定,若是没有参数那么能够什么都不写。
getExceptionTypes():Class[]:得到该方法可能抛出的异常类类型。
这几个方法的含义和用法都和Field的相似,这里再也不赘述。编程语言
4,建立实例
利用Class对象能够建立一个类型的实例。若是一个类型拥有无参数的构造函数,那么能够简单地调用Class.newInstance()方法建立一个实例。若是该类型没有无参数的构造函数,或者你但愿是用某个有参数的构造函数,那么能够首先使用getConstructors()、getConstructor(Class[] parameterTypes)和getDeclaredConstructors()、getDeclaredConstructor(Class[] parameterTypes)得到构造函数,这两个方法的返回值都使Constructor类型。特别注意的是,构造函数不能继承,所以你调用getConstructor也只能返回这个类型中定义的全部公有构造函数。
Constructor的使用方法和Method的相似,它也存在getParameterTypes()方法和getExceptionTypes()方法,不一样的是,它使用newInstance(Object... args)来调用一个构造函数,注意newInstance不须要实例对象,由于这个时候你还没建立出来这个实例呢函数