最近学习了Java中的反射.在此做以下总结:
反射可以在程序运行过程中动态获取类的相关信息,包括类由哪个类加载器进行加载,类中的成员变量,成员方法,访问修饰符,返回值类型,构造方法等等;
第一种方法在Java框架中使用最频繁,有了Class对象后,就可以调用Class对象的相关方法来获取类的所有的信息;
拿获取方法做示例如下:
现有实体类Student和Person:
package com.qcc.reflect.entity;
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person() {
super();
// TODO Auto-generated constructor stub
}
public void method1() {
System.out.println("person类: method1()");
}
private int method2() {
return 0;
}
}
package com.qcc.reflect.entity;
public class Student extends Person {
private int stuno;
public int getStuno() {
return stuno;
}
public void setStuno(int stuno) {
this.stuno = stuno;
}
public Student() {
super();
// TODO Auto-generated constructor stub
}
public String method3(){
return "Student类的公有无参方法method3()";
}
private void method4(int age){
System.out.println("Student类的私有带参方法method4(int age),实际参数为:" + age);
}
}
可以获取当前类以及父类中所有的公有方法;
@Test
public void testGetMethods() throws Exception {
Class cls = Class.forName("com.qcc.reflect.entity.Student");
Method[] methods = cls.getMethods();
for(Method method: methods) {
System.out.println(method);
}
}
运行结果:
public java.lang.String com.qcc.reflect.entity.Student.method3()
public void com.qcc.reflect.entity.Student.setStuno(int)
public int com.qcc.reflect.entity.Student.getStuno()
public java.lang.String com.qcc.reflect.entity.Person.getName()
public void com.qcc.reflect.entity.Person.setName(java.lang.String)
public void com.qcc.reflect.entity.Person.method1()
public int com.qcc.reflect.entity.Person.getAge()
public void com.qcc.reflect.entity.Person.setAge(int)
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
可以获取当前类中声明的所有方法(包括私有方法);
/**
* 获取当前类中所有声明的方法,包括私有方法
* @throws Exception
*/
@Test
public void testDeclaredMethods() throws Exception{
Class cls = Class.forName("com.qcc.reflect.entity.Student");
Method[] methods = cls.getDeclaredMethods();
for(Method method: methods) {
System.out.println(method);
}
}
运行结果如下:
public java.lang.String com.qcc.reflect.entity.Student.method3()
private void com.qcc.reflect.entity.Student.method4(int)
public void com.qcc.reflect.entity.Student.setStuno(int)
public int com.qcc.reflect.entity.Student.getStuno()
获取当前类及父类中指定的公有方法
@Test
public void testMethod() throws Exception{
Class cls = Class.forName("com.qcc.reflect.entity.Student");
Method method = cls.getMethod("method1");
method.invoke(cls.newInstance());
System.out.println(method);
}
运行结果如下:
person类: method1()
public void com.qcc.reflect.entity.Person.method1()
@Test
public void testDeclaredMethod() throws Exception{
Class cls = Class.forName("com.qcc.reflect.entity.Student");
Method method = cls.getDeclaredMethod("method4",int.class);
method.setAccessible(true);
method.invoke(cls.newInstance(),1);
System.out.println(method);
}
运行结果如下:
private void com.qcc.reflect.entity.Student.method4(int)
Student类的私有带参方法method4(int age),实际参数为:1
public Method getMethod(Class cls, String methodName, Class[] classes) throws Exception {
Method method = null;
try {
method = cls.getDeclaredMethod(methodName, classes);
} catch (NoSuchMethodException e) {
try {
method = cls.getMethod(methodName, classes);
} catch (NoSuchMethodException e1) {
if(cls.getSuperclass() != null) {
method = getMethod(cls.getSuperclass(), methodName, classes);
} else {
throw new Exception("所有的父类中都找不到" + methodName + "方法");
}
}
}
return method;
}
编写测试代码,测试getMethod()方法,因method1,method2,method3三个方法都没有参数列表,因此使用循环统一测试(纯属省事.):
@Test
public void testGetMethod() throws Exception{
Class cls = Class.forName("com.qcc.reflect.entity.Student");
String methodName = null;
for(int i=1; i<4; i++) {
methodName = "method" + i;
Method method = getMethod(cls, methodName, null);
System.out.println(method);
System.out.println("---------------------");
}
methodName = "method4";
Method method = getMethod(cls, methodName,new Class[]{int.class});
System.out.println(method);
}
运行结果:
public void com.qcc.reflect.entity.Person.method1()
---------------------
private int com.qcc.reflect.entity.Person.method2()
---------------------
public java.lang.String com.qcc.reflect.entity.Student.method3()
---------------------
private void com.qcc.reflect.entity.Student.method4(int)
Field的获取方式与Method相同,也分以上四类.略.
package com.qcc.reflect.test;
/**
* 简单计算器类
* 提供加.减.乘.除 运算
* @author QianChaoChen 00002336
* @date 2016年9月30日 上午9:47:05s
*/
public interface CalcDao {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}
package com.qcc.reflect.test;
public class CalcDaoImpl implements CalcDao {
@Override
public int add(int i, int j) {
int r = i + j;
return r;
}
@Override
public int sub(int i, int j) {
int r = i - j;
return r;
}
@Override
public int mul(int i, int j) {
int r = i * j;
return r;
}
@Override
public int div(int i, int j) {
int r = i / j;
return r;
}
}
实现动态代理的步骤如下:
动态代理的单元测试方法如下:
@Test
public void testProxy(){
//创建被代理的对象(接口类型)
final CalcDao target = new CalcDaoImpl();
//通过代理类Prxoy的newProxyInstance()方法创建代理对象
CalcDao proxy = (CalcDao) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("方法【" + method.getName() + "】开始执行, 参数为" + Arrays.asList(args) + "...");
long start = System.currentTimeMillis();
Object result = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println("方法【" + method.getName() + "】执行完成,运算结果为:" + result + ", 用时" + (end - start) + "毫秒!");
return result;
}
});
proxy.add(10, 2);
proxy.sub(10, 2);
proxy.mul(10, 2);
proxy.div(10, 2);
}
}
proxy.add(10,2)是调用代理对象的add方法,它会去调用InvocationHandler的匿名内部类中的invoke方法,invoke方法中method.invoke(target,args)其实是调用被代理对象target的method方法,(此处就是反射在动态代理中最核心的部分)传递的参数数组是args,代理对象会在method.invoke()方法之前和之后做一些事情.
InvocationHandler接口的实现类中重写的invoke()方法也有三个参数,
proxy:正在生成的代理对象本身;
method:正在被调用的那个方法
args:被调用的方法中实际传递的参数;
result是调用目标方法后的返回值,如果方法本身没有返回值,则result为null.
以上测试代理对象的运行结果:
方法【add】开始执行, 参数为[10, 2]...
方法【add】执行完成,运算结果为:12, 用时0毫秒!
方法【sub】开始执行, 参数为[10, 2]...
方法【sub】执行完成,运算结果为:8, 用时0毫秒!
方法【mul】开始执行, 参数为[10, 2]...
方法【mul】执行完成,运算结果为:20, 用时0毫秒!
方法【div】开始执行, 参数为[10, 2]...
方法【div】执行完成,运算结果为:5, 用时0毫秒!