Java反射最大的好处就是能在运行期间,获得某个类的结构、成员变量,用来实例化,而不是仅仅靠new来实现。
一、获得Class对象
在Java中,每个类被加载之后,系统就会为类生成一个对象的Class对象,通过该Class对象就可以访问JVM中的这个类了。在Java程序中,获得Class对象通常有如下三种方式。
1.使用Class类的forName(String clazzName)静态方法。该方法需要传入字符串参数,该字符串参数的值是某个类的全限定类名(必须添加完整包名);
2.调用某个类的class属性,如Person.class将会返回Person类对象的Class对象;
3.调用某个对象的getClass()方法。该方法时java.lang.Object类中的一个方法,所有的Java对象都可以调用。
有了Class对象,我们就可以通过Class对象来获取其所对应类的详细信息,我们可以获取Class对应类所包含的构造器、方法、成员变量、注释、修饰符、所在包、类名等基本信息。
二、创建对象
通过反射来生成对象有如下两种方式:
1.使用Class对象的newInstance()方法来创建该Class对象对应类的实例,这种方式要求该Class对象的对应类有默认构造器,而执行newInstance()方法实际上是利用默认构造器来创建该类的实例。
package cn.gome.d_generic;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class ObjectPoolFactory {
//定义一个对象池,签名是对象名,后面是实际对象
private Map<String, Object> objectPool = new HashMap<String, Object>();
//方法一:定义一个创建对象的方法,只要传入字符串类名,程序可以根据该类名生成Java对象
private Object createObject (String clazzname) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
//根据字符串类获取对应的对象
Class<?> clazz = Class.forName(clazzname);
//使用clazz对象对应类的默认构造器创建实例
return clazz.newInstance();
}
//方法二:此方法根据指定文件来初始化对象池,根据配置文件来创建对象
public void initPool(String fileName) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException{
FileInputStream fis = new FileInputStream(fileName);
Properties props = new Properties();
props.load(fis);
for (String name : props.stringPropertyNames()) {
//key-value键值对,根据name获取对应的值
objectPool.put(name, createObject(props.getProperty(name)));
}
}
//方法三:从ObjectPool中取出指定name对应的对象
public Object getObject(String name){
return objectPool.get(name);
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException {
ObjectPoolFactory pFactory = new ObjectPoolFactory();
pFactory.initPool("obj.txt");
System.out.println(pFactory.getObject("a"));
System.out.println(pFactory.getObject("b"));
}
}
2.先使用Class对象获取指定的Constructor对象,在调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例。通过这种方式可以选择使用指定的构造器来创建实例。
package cn.gome.d_generic;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import javax.swing.JFrame;
public class CreateJFrame {
public static void main(String[] args) throws NoSuchMethodException,
SecurityException, InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
// 获取JFrame对应的Class对象
Class<?> clazz = JFrame.class;
// 获取JFrame中带一个字符串参数的构造器
Constructor cor = clazz.getConstructor(String.class);
// 调用Constructor的newInstance方法创建对象
Object object = cor.newInstance("测试窗口");
//输出JFrame对象
System.out.println(object);
}
}
三、操作对象
1.调用方法
当获得某个类对应的Class对象后,就可以通过该Class对象的getMethods()方法或者getMethod()方法来获取全部方法或指定方法,每当Method对象对应一个方法,获得Method对象后,程序就可以通过Method的invoke()方法来调用它。
Object invoke(Object obj,Object... args):该方法中的obj是执行该方法的主调(方法所在类的实例),后面的args是执行该方法时传入方法的实参。
2.访问成员变量
通过Class对象的getFields()或者getField()方法可以获取该类所包括的全部成员变量或指定成员变量。Field对象提供了如下两组方法来读取或设置成员变量值。
getXxx(Object obj):获取obj对象的该成员变量的值。
setXxx(Object obj,Xxx val):将obj对象的该成员变量设置成val值。
四、操作数组
在java.lang.reflect包下还提供了一个Array类,Array对象可以代表所有的数组。程序可以通过使用Array来动态地创建数组,操作数组元素。
package cn.gome.d_generic;
import java.lang.reflect.Array;
public class ArrayTest {
public static void main(String[] args) {
//创建一个元素类型为String,长度为10的数组
Object arrObject = Array.newInstance(String.class,10);
//依次为arrObject数组中index为5、6、7的元素赋值
Array.set(arrObject, 5, "周周");
Array.set(arrObject, 6, "江江");
Array.set(arrObject, 7, "霄霄");
//依次取出arrObject数组中index为5、6、7的元素的值
Object o1 = Array.get(arrObject, 5);
Object o2 = Array.get(arrObject, 6);
Object o3 = Array.get(arrObject, 7);
//输出arrObject数组中index为5、6、7的元素的值
System.out.println(o1);
System.out.println(o2);
System.out.println(o3);
}
}