反射机制是Java语言提供的一种基础功能。我们在使用IDE编写源代码,之后编译成为对应的class文件,每个类被加载之后,系统都会为该类生成一个对应的class对象,通过这个class对象就可以访问到JVM中的这个类。什么叫反射呢?在程序运行时期,我们可以通过反射直接操作类和对象。比如获取某个类的定义,获取类的属性和方法,调用方法和构造器,甚至可以修改类的属性的访问权限。
获取类对象的三个途径:
- 使用class类的forName(String className)静态方法,该方法需要传入类的全限定类名(必须添加完整包名)
- 调用某个类的class属性来获取对应的class对象。比如Person.class将会返回Person类的class对象
- 调用某个对象的getClass()方法,该方法是java.lang.Object类的一个方法,返回该对象所属类对应的class对象
对于第一种方式和第二种方式都是直接根据类来取得该类的Class对象,相比之下,第二种方式更有优势,优点如下:
- 代码更安全,程序在编译阶段就可以检查需要访问的Class对象是否存在
- 程序性能更好。因为这种方式无需调用方法
通过Class对象可以获取类的方法(Method),构造器(Constructor),成员变量(Field),这三个类都在java.lang.reflect包下,并实现了 java.lang.reflect.Member接口。程序可以通过Method对象来执行对应的方法,通过Constructor对象来调用对应的构造器创建对象,通过Field对象直接访问并修改对象的成员变量。
下面有一个示例:
从配置文件中获取类的全限定类名和类的属性名称,利用反射机制创建配置文件的对象池,这个对象池是根据配置文件的key来获得对象的。
配置文件的形式:key=classpathname key%参数=参数值,具体可以看下面的程序
b=java.util.Date
a=javax.swing.JFrame
a%title=Test File
person=reflect.TestPerson
person%name=zhangsan
package reflect;
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;
//创建的测试类TestPerson
class TestPerson{
private String name;
private int age;
public TestPerson() {};
public String toString() {
return "[name:"+name+",age:"+age+ "]";
}
public void setName(String name) {
this.name = name;
}
}
public class ExtendedObjectPoolFactory {
//创建对象池
private Map<String, Object> objectPool = new HashMap<>();
//创建配置文件对象
private Properties config = new Properties();
//配置对象加载配置文件
public void loadPropertyFile(String fileName) {
try(
FileInputStream fis = new FileInputStream(fileName)) {
config.load(fis);
}catch(IOException e) {
System.out.println("read configuration:" + fileName + "exception");
}
}
//根据类的全路径名创建class对象
private Object createObject(String className)
throws Exception,IllegalAccessException,ClassNotFoundException{
Class<?> classObject = Class.forName(className);
System.out.println(classObject);
return classObject.getConstructor().newInstance();
}
//读取配置文件的内容,创建对象,然后加入对象池
public void getObjectPool() throws Exception {
for(String name : config.stringPropertyNames()) {
if(!name.contains("%")) {
objectPool.put(name, createObject(config.getProperty(name)));
}
}
}
//根据调用类的属性名str来调用setter方法设置对应的属性
public void ivokeSetMethod(String str) throws IllegalAccessException,
InvocationTargetException, NoSuchMethodException {
for(String name : config.stringPropertyNames()) {
if(name.contains("%") && str != null && ! "".equals(str)) {
String[] objAndProp = name.split("%");
if(objAndProp[0].equals(str)) {
Object target= getObject(objAndProp[0]);
String mtdName = "set" +
objAndProp[1].substring(0,1).toUpperCase() +
objAndProp[1].substring(1);
Class<?> targetClass = target.getClass();
//获取希望调用的setter方法,如果是Person类的属性name,mtdName为setName,
Method mtd = targetClass.getMethod(mtdName, String.class);
//执行setter方法,将config.getProperty(name)作为参数
mtd.invoke(target, config.getProperty(name));
}
}
}
}
//根据name从对象池获取对象
public Object getObject(String name) {
return objectPool.get(name);
}
public static void main(String[] args) throws Exception {
ExtendedObjectPoolFactory pf = new ExtendedObjectPoolFactory();
String key = "a";
String configFileName = "obj.txt";
//加载配置文件
pf.loadPropertyFile(configFileName);
//获取配置文件配置的对象池
pf.getObjectPool();
//设置配置文件的key值
pf.ivokeSetMethod("person");
System.out.println(pf.getObject(key));
System.out.println(pf.getObject("person"));
}
}
反射是程序运行时期类对象获取这个类的属性,方法,构造器,我们可以根据这个反射做什么事情呢?最典型的应用就是动态代理。动态代理是程序在运行时期动态构建,动态调用代理方法调用的机制。比如远程调用的RPC机制,Spring编程的切面编程 (AOP)。通过代理可以让实现者和调用者之间解耦,下面是JDK Proxy代理的一个例子:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class TestProxy {
public static void main(String[] args) {
//创建HelloImpl对象
HelloImpl hello = new HelloImpl();
MyInvocationHandler handler = new MyInvocationHandler(hello);
// 构造代理实例
Hello proxyHello = (Hello) Proxy.newProxyInstance(
HelloImpl.class.getClassLoader(),
HelloImpl.class.getInterfaces(),
handler);
// 调用代理方法
proxyHello.sayHello();
proxyHello.saybye();
}
}
//实现的顶层接口
interface Hello {
void sayHello();
void saybye();
}
//实现接口,实现具体的业务代码
class HelloImpl implements Hello {
@Override
public void sayHello() {
System.out.println("Hello World");
}
@Override
public void saybye() {
System.out.println("byebye");
}
}
class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("Invoking start!");//可以实现通用代码1
Object result = method.invoke(target, args);
System.out.println("invoking end!");//可以实现通用代码2
return result;
}
}