前言:
Java反射机制可以让我们在编译期(Compile Time)之外的运行期(Runtime)检查类,接口,变量以及方法的信息。反射还可以让我们在运行期实例化对象,调用方法,通过调用get/set方法获取变量的值。new 创建的对象是静态加载类,在编译时刻就要加载所有可能要用到的类,而反射创建的对象是是是动态加载类,在运行时刻加载需要用的类。
class对象
在程序运行之期间,JAVA运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行,保存这些信息的类被称为Class.简单的说就是,所有类其实都是java.lang.Class的实例,这个对象被称为类的类类型。
在你想检查一个类的信息之前,你首先需要获取类的Class对象。
获取类的Class的对象有三种方式:
第一种表达方式:
Class c1=类名.class;//这也可以说明,任何一个类都有一个隐含的静态成员变量class
第二种表达方式:
已经知道该类的对象通过getClass方法
Person person=new Person();
Class c2=person.getClass();
第三种表达方式:
Class c3=null;
c3=Class.forName("类的全路径名");
具体代码如下:
package reflection;
class Demo1{
//。。。。。
}
class Demo2{
public static void main(String[] args) {
Class<?> demo1=null;
Class<?> demo2=null;
Class<?> demo3=null;
try{
//一般尽量采用这种形式
demo1=Class.forName("reflection.Demo1");
}catch(Exception e){
e.printStackTrace();
}
demo2=new Demo1().getClass();
demo3=Demo1.class;
System.out.println(demo1);
System.out.println(demo2);
System.out.println(demo3);
运行结果:
class reflection.Demo1
class reflection.Demo1
class reflection.Demo1
class reflection.Demo1
class reflection.Demo1
利用反射分析类的能力
在java.lang.reflect包中有三个类Field,Methof和Constructor分别用于描述类的域、方法和构造器。这三个类都有一个叫做getName的对象,用来返回项目的名称。Field类有一个getType方法,用来返回描述所属类型的Class。Method和Constructor类有能够报告参数类型的方法,Method类还有一个可以报告返回类型的方法。
Class类中的getFields,getMethods和getConstructors方法将分别返回类提供的public域、方法和构造器数组,其中包括超类的共有成员。Class类的getDeclareFields、getDeclareMethods和getDeclareConstructors方法将分别返回类中声明的全部域、方法和构造器,其中包括私有和保护成员,但不包括超类中的成员。
获取构造函数:
利用Java的反射机制你可以检查一个类的构造方法,并且可以在运行期创建一个对象。这些功能都是通过java.lang.reflect.Constructor这个类实现的。
若想通过类的的构造方法来生成对象,有两种方式:
1.想获得Class 对象,然后通过该Class对象的newInstance()方法直接生成即可:Class<?> classType=String.class;Object obj=classType.newInstance();
2.先获得Class对象,然后通过该对象获得对应的Constructor对象,在通过该Constructor对象的newInstance()方法生成:
-
.
Constructor getConstructor(Class[] params)
公共构造函数 -
. Constructor[] getConstructors()
-- 获得类的所有公共构造函数 -
. Constructor getDeclaredConstructor(Class[] params)
-- 本类声明的构造函数 -
. Constructor[] getDeclaredConstructors()
-- 获得类的所有构造函数 - 其中区别在于第一种不能解决构造方法传递参数
示例代码:
Class c=obj.getClass();
//获取所有的public的构造函数
Constructor[] cs=c.getConstructors();
//得到自己生命的构造函数
Constructor[] cs1=c.getDeclaredConstructors();
for(Constructor constructor:cs)
{
System.out.print(constructor.getName()+"(");
//获取构造函数的参数列表--得到 是参数列表的类类型
Class [] paramTypes=constructor.getParameterTypes();
for(Class class1:paramTypes)
{
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
获取字段的信息:
-
Field getField(String name)
-- 获得命名的公共字段 -
Field[] getFields()
-- 获得类的所有公共字段 -
Field getDeclaredField(String name)
-- 获得类声明的命名的字段 -
Field[] getDeclaredFields()
-- 获得类声明的所有字段 - 示例代码:
Field[] fs=c.getFields();
Field[] fs2=c.getDeclaredFields();
for(Field field:fs)
{
//得到成员变量的类型的类类型
Class fieldType=field.getType();
String typeName=fieldType.getName();
//得到成员变量的名称
String fieldName=field.getName();
System.out.println(typeName+" "+fieldName);
获取方法信息
获取方法信息的class反射调用和用于构造函数和字段的调用非常类似
-
Method getMethod(String name, Class<?>...parameterTypes)
-- 获得命名的公共方法 -
Method[] getMethods()
-- 获得类的所有公共方法 -
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
-- 获得类声明的命名的方法 -
Method[] getDeclaredMethods()
-- 获得类声明的所有方法 - 示例代码:
-
public static void print(Object obj) { Class c=obj.getClass(); System.out.println("类名是"+c.getName()); Method[] ms =c.getMethods();//获取的是所有的public函数,包括父类继承而来的 //Method[] ms1=c.getDeclaredMethods();//获取的是所有该类自己声明的方法 for(int i=0;i<ms.length;i++) { //得到方法的返回值类型的类类型 Class returnType= ms[i].getReturnType(); System.out.print(returnType.getName()+" "); //得到方法的名称 System.out.print(ms[i].getName()+"("); //获取参数类型--得到是参数列表的类型的类类型 Class[] paramTypes=ms[i].getParameterTypes(); for(Class class1:paramTypes) { System.out.print(class1.getName()+","); } System.out.println(")"); } }
总结:
反射是一种强大的工具。它使您能够创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代表链接。
反射有两个缺点。第一个是性能问题。当用于字段和方法接入时反射要远慢于直接代码。性能问题的程度取决于程序中是如何使用
反射的。如果它作为程序运行中相对很少涉及的部分,缓慢的性能将不会是一个问题。
仅反射在性能关键的应用的核心逻辑中使用时性能问题才变得
至关重要。
许多应用更严重的一个缺点是使用反射会模糊程序内部实际要发生的事情。程序人员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术会带来维护问题。反射代码比相应的直接代码更复杂,正如性能比较的代码实例中看到的一样。解决这些问题的最
佳方案是保守地使用反射-- 仅在它可以真正增加灵活性的地方 。
参考文献:1,《java编程思想》
2,http://www.ibm.com/developerworks/cn/java/j-dyn0603/