所谓的反射机制就是java语言在运行时拥有的一项自观的能力。通过这种能力可以彻底地了解自身的情况为下一步的动作做准备。下面介绍java的反射机制。
Java的反射机制的实现要借助于4个类:Class、Constructor、Field、Method,其中Class代表的是类对象,Constructor代表类的构造器对象,Field代表类的属性对象,Method代表类的方法对象。通过这四个对象我们可以粗略看到一个类的各个组成部分。
Class:
程序运行时,java运行时系统会对所有的对象进行运行时类型的处理。这项信息记录了每个对象所属的类,虚拟机通常使用运行时类型信息选择正确的方法来执行。我们可以借助Object类中定义的getClass()方法要得到指定对象的类对象,然后通过分析这个对象得到我们想要的信息,而Class本身又有很多重要的方法,此处重点将和Constructor,Field,Method类有关系的方法:
ü Class.forName(“类名”) 表示载入指定的类,调用getDeclaredMethods来
获取这个类中定义的方法列表
实例:
package com.yilong.reflect;
import java.lang.reflect.Method;
public class Test1 {
public static void main(String[] args) {
try {
Class c = Class.forName(args[0]);
Method m[] = c.getDeclaredMethods();
for(int i=0; i<m.length; i++) {
System.out.println(m[i].toString());
}
} catch(Throwable e) {
e.printStackTrace();
}
}
}
执行方法:
编译:javac –d . Test1.java
运行:java com.yilong.reflect.Test1 java.util.ArrayList
Constructor:
Constructor getConstructor(Class[] params) – 获得使用特殊的参数类型的公共构造函数;
Constructor[] getConstructors() – 获得类的所有公共构造函数;
Constructor getDeclaredConstructor(Class[] params) – 获得使用特定参数类型的构造函数(上面是公共);
Constructor[] getDeclaredConstructors() – 获得类的所有构造函数(上面是”公共”);
Field:
Field getField(String name) – 获得命名的公共字段;
Field[] getFields() – 获得类的所有公共字段;
Field getDeclaredField(String name) – 获得类声明的命名的字段;
Fidld[] getDeclaredFields() – 获得类声明的所有字段。
Method:
Method getMethods(String name, Class[] params) – 使用特定的参数类型,获得命名的公共方法;
Method[] getMethods() – 获得类的所有公共方法;
Method getDeclaredMethod(String name, Class[] params) – 使用特定的参数类型,获得声明的命名的方法;
Method[] getDeclaredMethods() – 获得类声明的所有的方法
要向使用JAVA的Reflection,需要遵循三个步骤:
第一步:获得你想要操作的类的java.lang.Class对象。在运行中的JAVA程序中,用
java.lang.Class类来描述类和接口等;
下面是获得Class对象的一些方法:
Class c = Class.forName(“java.lang.String”);
Class c = int.class;
Class c = Integer.TYPE;
第二步:调用诸如getDeclaredMethods的方法,取得该类中定义的所有方法的列表;
第三步:使用reflection的API来操作这些信息。
下面是一些具体的实例:
Java中使用Class.forName(“”).newInstance();对对象进行实例化,同时还需要掌握对其对象方法的调用方法。
下面结合具体例子进行分析:
Ø 文件com.yilong.test.instance.NewClass.java
package com.yilong.test.instance;
public class NewClass {
public boolean method() {
System.out.println("ok!");
return true;
}
}
Ø 文件com.yilong.test.instance.Main.java
package com.yilong.test.instance;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws Exception {
Object clazz = Class
.forName("com.yilong.test.instance.NewClass")
.newInstance();
Method method = clazz.getClass().getMethod("method");
Class returntype = method.getReturnType();
method.invoke(clazz);
System.out.println(method.getName() + "|"
+ returntype.getName());
}
}
注意事项1:上面的getMethod()方法只适用于调用的方法没有参数的情况,如果方法要传递参数,还需要指定参数的类型(因为有重载),对应地,method.invoke()方法也是要传递具体的参数值的。
ü public Method getMethod(String name, Class<?>… parameterTypes)
throws NoSuchMethodException,SecurityException
ü public Object invoke(Object obj, Object... args)
throws IllegalAccessException,
IllegalArgumentException, InvocationTargetException
ü 另外,还有一个重载的方法是可以拿到一个类里定义的所有的方法:
Class clazz = Class.forName("com.yilong.test.instancemethod.NewClass");
Method[] methods = clazz.getMethods();
//clazz只是拿到该类,还没实例化,如果实例化了,那么就要调用//newInstance.getClass().getMethods();
下面是囊括了比较多的情况的例子
Ø 文件NewClass.java
package com.yilong.test.instance;
public class NewClass {
public void method0() {
System.out.print("0个参数的方法调用成功!");
}
public boolean method1(int id) {
System.out.print("1个参数的方法调用成功!");
return true;
}
public String method2(int id, String name) {
System.out.print("2个参数的方法调用成功!");
return "success";
}
public int method3(String[] strs) {
System.out.print("参数为数组的方法调用成功!");
return 1;
}
}
Ø 文件Main.java
package com.yilong.test.instance;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws Exception {
Object clazz = Class.forName
("com.yilong.test.instance.NewClass").newInstance();
//调用0个参数的方法
Method method0 = clazz.getClass().getMethod("method0");
Class returntype0 = method0.getReturnType();
method0.invoke(clazz);
System.out.println(" -- " + method0.getName() + "|" +
returntype0.getName());
//调用1个参数的方法
Class[] parameterTypes1 = new Class[1];
parameterTypes1[0] = int.class;
Method method1 = clazz.getClass().getMethod("method1",
parameterTypes1);
Class returntype1 = method1.getReturnType();
method1.invoke(clazz, 1);
System.out.println(" -- " + method1.getName() + "|" +
returntype1.getName());
//调用2个参数的方法
Class[] parameterTypes2 = new Class[2];
parameterTypes2[0] = int.class;
parameterTypes2[1] = String.class;
Method method2 = clazz.getClass().getMethod("method2",
parameterTypes2);
Class returntype2 = method2.getReturnType();
method2.invoke(clazz, 2, "ok");
//或者
Object[] arguments = new Object[2];
arguments[0] = 2;
arguments[1] = "ok";
method2.invoke(clazz, arguments);
System.out.println(" -- " + method2.getName() + "|" +
returntype2.getName());
//调用参数为数组的方法
Class[] parameterType3 = new Class[1];
parameterType3[0] = String[].class;
Method method3 = clazz.getClass().getMethod("method3",
parameterType3);
Class returntype3 = method3.getReturnType();
Object[] arguments3 = new Object[1];
String[] strs = new String[2];
strs[0] = "aa";
strs[1] = "bb";
arguments[0] = strs;
method3.invoke(clazz, arguments3);
System.out.println(" -- " + method3.getName() + "|" +
returntype3.getName());
}
}
注意事项2:通过Class.forName("com.yilong.test.instance.NewClass");方法得到的Class只能是放在classPath下面的class文件生成的,即对应项目中的bin目录下的文件,也就是说前面的目录已经默认是”项目根路径 + bin”。而通过netURL方式new出来得到Class则不一定,具体实例如下:
System.out.println(System.getProperty("user.dir"));
//D:/MyEclipse/Proxy1
URL[] urls = new URL[] {new URL("file:/" +
System.getProperty("user.dir") + "/src")};
URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass("com.yilong.designpattern.proxy.TimeProxy");
System.out.println(c);
项目中目录状态如下:
注意事项3:上述的newInstance()方法只能实例化没有参数的构造方法,要实例化有参数的构造方法,可以:
Constructor ctr = c.getConstructor(Moveable.class);//指定参数类型
Moveable m = (Moveable)ctr.newInstance(new Tank());//传递参数
Ø 文件Main.java
package com.yilong.test.instancemethod;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Main {
public static void main(String[] args) {
try {
Class clazz = Class.forName
("com.yilong.test.instancemethod.NewClass");
System.out.println(clazz);
//调用1个参数的构造方法
Constructor ctr1 = clazz.getConstructor(int.class);
System.out.println(ctr1);
ctr1.newInstance(1);
//调用2个参数的构造方法
Constructor ctr2 = clazz.getConstructor(int.class,
String.class);
System.out.println(ctr2);
ctr2.newInstance(2, "ok");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
Ø 文件NewClass.java
package com.yilong.test.instancemethod;
public class NewClass {
public NewClass(int id) {
System.out.println("1个参数的构造方法调用成功!");
}
public NewClass(int id, String str) {
System.out.println("2个参数的构造方法调用成功!");
}
public void method(String[] strs, int i) {
System.out.println("success!");
}
}