反射
使用反射获取程序运行时的对象和类的真实信息。
获取 Class 对象
每个类被加载之后,系统会为该类生成一个对应的 Class 对象,通过该 Class 对象可以访问到 JVM 中的这个类。
使用 Class 类的 forName(String clazzName) 静态方法。字符串参数的值是某个类的全限定类名,必须包含完整的包名
调用某个类的 class 属性
调用某个对象的 getClass() 方法。该方法是 java.lang.Object 类中的一个方法,所有的 Java 对象都可以调用,返回该对象所属类对应的 Class 对象
获取 Class 对象中信息
Class 类提供了大量的实例方法来获取该 class 对象所对应的类的详细信息。更多请参考 API。
import java.lang.reflect.*;
import java.lang.annotation.*;
public class ClassTest {
private ClassTest() {
}
public ClassTest(String name) {
System.out.println("执行有参数的构造器");
}
public void info() {
System.out.println("执行无参数的info方法");
}
public void info(String str) {
System.out.println("执行有参数的info方法" + ",其 str 参数值: " + str);
}
class Inner {
}
public static void main(String[] args) throws Exception {
Class clazz = ClassTest.class;
// 获取 clazz 对象所对应类的全部构造器
Constructor>[] ctros = clazz.getDeclaredConstructors();
System.out.println("ClassTest 的全部构造器如下: ");
for (Constructor c : ctros) {
System.out.println(c);
}
// 获取 clazz 对象所对应类的全部 public 构造器
Constructor>[] publicCtors = clazz.getConstructors();
System.out.println("ClassTest的全部public构造器如下:");
for (Constructor c : publicCtors) {
System.out.println(c);
}
// 获取 clazz 对象所对应类的全部 public 方法
Method[] mtds = clazz.getMethods();
System.out.println("ClassTest 的全部 public 方法如下: ");
for (Method md : mtds) {
System.out.println(md);
}
// 获取 clazz 对象所对应类的指定方法
System.out.println("ClassTest 里带一个字符串参数的 info 方法为:" + clazz.getMethod("info", String.class));
// 获取 clazz 对象所对应类的全部注解
Annotation[] anns = clazz.getAnnotations();
System.out.println("ClassTest 的全部 Annotation 如下: ");
for (Annotation an : anns) {
System.out.println(an);
}
// 获取 clazz 对象所对应类的全部内部类
Class>[] inners = clazz.getDeclaredClasses();
System.out.println("ClassTest 的全部内部类如下: ");
for (Class c : inners) {
System.out.println(c);
}
// 使用 Class.forName() 方法加载 ClassTest 的 Inner 内部类
Class inClazz = Class.forName("ClassTest$Inner");
// 访问该类所在的外部类
System.out.println("inClazz 对应类的外部类为: " + inClazz.getDeclaringClass());
System.out.println("ClassTest 的包为:" + clazz.getPackage());
System.out.println("ClassTest 的父类为:" + clazz.getSuperclass());
}
}
应用
Class 对象可以获得对应类的方法(由 Method 表示)、构造器(由 Constructor 表示)、成员变量(由 Field 对象表示),且这个三个类都实现了 java.lang.reflect.Member 接口。程序可以通过 Method 对象来执行对应的方法,通过 Constructor 对象来调用对应的构造器创建实例,通过 Field 对象直接访问并修改对象的成员变量值。
创建对象
使用 Class 对象的 newInstance() 方法来创建 Class 对象对应类的实例。要求该 Class 对象的对应类有默认构造器
先使用 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance() 方法来创建该 Class 对象对应类的实例。这种方式可以选择使用指定的构造器来创建实例
方式一
实现了一个简单的对象池,该对象池会根据配置文件读取 key-value 对,然后创建这些对象并放入 HashMap 中
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class ObjectPoolFactory {
private Map objectPool = new HashMap<>();
private Object createObject(String clazzName) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
Class> clazz = Class.forName(clazzName);
// 使用 Class 对象对应的类的默认构造器
return clazz.newInstance();
}
public void initPool(String fileName) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
try (
FileInputStream fis = new FileInputStream(fileName)
) {
Properties props = new Properties();
props.load(fis);
for ( String name: props.stringPropertyNames()) {
objectPool.put(name, createObject(props.getProperty(name)));
}
} catch (IOException ex) {
System.out.println("读取" + fileName + "异常");
}
}
public Object getObject(String name) {
return objectPool.get(name);
}
public static void main(String[] args) throws Exception{
ObjectPoolFactory pf = new ObjectPoolFactory();
pf.initPool("obj.txt");
System.out.println(pf.getObject("a"));
System.out.println(pf.getObject("b"));
}
}
/*
obj.txt 内容:
a=java.util.Date
b=javax.swing.JFrame
*/
方式二
import java.lang.reflect.Constructor;
public class CreateJFrame {
public static void main(String[] args) throws Exception {
Class> jframeClazz = Class.forName("javax.swing.JFrame");
// 选择使用指定的构造器
Constructor ctor = jframeClazz.getConstructor(String.class);
Object obj = ctor.newInstance("测试窗口");
System.out.println(obj);
}
}
调用方法
每个 Method 对象对应一个方法,获得 Method 对象后,就可以通过该 Method 来调用它对应的方法。
Method 包含一个 invoke() 方法,该方法的签名如下:
- Object invoke(Object obj, Object... args):该方法中的 obj 是执行方法的主调(即类的实例对象),后面的 args 是执行该方法的实参
下面是对之前的对象工厂池进行增强,允许在配置文件中增加配置对象的成员变量值,对象池工厂会读取该对象配置的成员变量值,并利用该对象对应的 setter 方法设置成员变量的值:
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class ExtendedObjectPoolFactory {
// 定义一个对象池,前面是对象名,后面是实际的对象
private Map objectPool = new HashMap<>();
private Properties config = new Properties();
// 从指定文件中初始化 Properties 对象
public void init(String fileName) {
try (
FileInputStream fis = new FileInputStream(fileName);
) {
config.load(fis);
} catch (IOException ex) {
System.out.println("读取" + fileName + "异常");
}
}
// 定义创建对象的方法
private Object createObject(String clazzName) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
// 根据字符串来获取对应的 Class 对象
Class> clazz = Class.forName(clazzName);
// 使用 clazz 对应类的默认构造器创建实例
return clazz.newInstance();
}
// 初始化对象池
public void initPool() throws InstantiationException, IllegalAccessException, ClassNotFoundException {
for (String name : config.stringPropertyNames()) {
if (!name.contains("%")) {
objectPool.put(name, createObject(config.getProperty(name)));
}
}
}
// 根据属性文件来调用指定对象的 setter 方法
public void initProperty() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
for (String name : config.stringPropertyNames()) {
if (name.contains("%")) {
String[] objAndProp = name.split("%");
Object target = getObject(objAndProp[0]);
String mtdName = "set" + objAndProp[1].substring(1);
// 通过 target 的 getClass() 获取它的实现类所对应的 Class 对象
Class> targetClass = target.getClass();
// 获取希望调用的 setter 方法
Method mtd = targetClass.getMethod(mtdName, String.class);
// 通过 Method 的 invoke 方法执行 setter 方法
mtd.invoke(target, config.getProperty(name));
}
}
}
public Object getObject(String name) {
// 从 objectPool 中取出指定 name 对应的对象
return objectPool.get(name);
}
public static void main(String[] args) throws Exception {
ExtendedObjectPoolFactory epf = new ExtendedObjectPoolFactory();
epf.init("extObj.txt");
epf.initPool();
epf.initProperty();
System.out.println(epf.getObject("a"));
}
}
/* extObj.txt 内容
a=java.util.Date
b=javax.swing.JFrame
# set the title of a
a%title=Test Title
*/
PS:当通过 Method 的 invoke() 方法来调用对应的方法时,Java 会要求程序必须有调用该方法的权限。如果需要调用某个对象的 private 方法,则可以先调用 Method 对象的如下方法:
- setAccessible(boolean flag):值为 true,表示该 Method 在使用时取消访问权限检查
访问成员变量值
Filed 提供如下两组方法来读取或设置成员变量值:
getXxx(Object obj):获取 obj 对象的该成员变量的值。此处的 Xxx 对应 8 中基本类型。如果成员变量的类型是引用类型,则直接使用 get
setXxx(Object obj, Xxx val):将 obj 对象的成员变量值设为 val 值。此处的 Xxx 对应 8 中基本类型。如果成员变量的类型是引用类型,则直接使用 set
public class Person {
private String name;
private int age;
public String toString() {
return "Person[name:" + name + ", age:" + age + "]";
}
}
import java.lang.reflect.Field;
public class FieldTest {
public static void main(String[] args) throws Exception {
Person p = new Person();
Class personClazz = Person.class;
Field nameField = personClazz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(p, "crazy");
Field ageField = personClazz.getDeclaredField("age");
ageField.setAccessible(true);
ageField.setInt(p, 30);
System.out.println(p);
}
}
泛型在反射中的应用
在反射中使用泛型,反射生成的对象就不需要进行强制类型转换。
import java.util.Date;
public class CrazyitObjectFactory {
public static T getinstance(Class cls) {
try {
return cls.newInstance();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
// 获取实例后无需进行类型转换
Date d = CrazyitObjectFactory.getinstance(Date.class);
}
}
欢迎关注我的公众号