Java反射机制的奥妙

反射机制是Java语言提供的一种基础功能。我们在使用IDE编写源代码,之后编译成为对应的class文件,每个类被加载之后,系统都会为该类生成一个对应的class对象,通过这个class对象就可以访问到JVM中的这个类。什么叫反射呢?在程序运行时期,我们可以通过反射直接操作类和对象。比如获取某个类的定义,获取类的属性和方法,调用方法和构造器,甚至可以修改类的属性的访问权限。

获取类对象的三个途径:

  1. 使用class类的forName(String className)静态方法,该方法需要传入类的全限定类名(必须添加完整包名)
  2. 调用某个类的class属性来获取对应的class对象。比如Person.class将会返回Person类的class对象
  3. 调用某个对象的getClass()方法,该方法是java.lang.Object类的一个方法,返回该对象所属类对应的class对象

对于第一种方式和第二种方式都是直接根据类来取得该类的Class对象,相比之下,第二种方式更有优势,优点如下:

  1. 代码更安全,程序在编译阶段就可以检查需要访问的Class对象是否存在
  2. 程序性能更好。因为这种方式无需调用方法

通过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;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值