一. 什么是JAVA反射
java反射机制是在程序运行状态中,对于任何一个类都能够知道这个类的属性和方法。对于任何一个对象都能够调用它的任意一个方法。Java反射机制运行程序判断分析任何一个类的结构,包括成员方法和变量,并调用任意一个对象的方法。
二. JAVA反射的实例
在JDK中,主要由以下类来实现Java反射机制,这些类(除了第一个)都位于java.lang.reflect包中
Class类:代表一个类,位于java.lang包下。
Field类:代表类的成员变量(成员变量也称为类的属性)。
Method类:代表类的方法。
Constructor类:代表类的构造方法。
Class类
Class类是用来保存运行时类型信息的类。每个类(型)都有一个Class对象。运行程序时,Java虚拟机(JVM)首先检查是否所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。一般当某个类的Class对象被载入内存时,它就可以用来创建这个类的所有对象。
常用的获取Class对象的3种方式:
1.使用Class类的静态方法。例如:
Class.forName("java.lang.String");
2.使用类的.class语法。如:
String.class;
3.使用对象的getClass()方法。如:
String str = "aa";
Class<?> classType1 = str.getClass();
Field类
再来看看通过class对象来获取类的属性
private String strName;
private int sum;
public String strTest;
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
ReflectTest rTest=new ReflectTest();
Class<?> clsType = ReflectTest.class;
//获得当前类和父类中的public类型的所有属性
Field[] fields=clsType.getFields();
for(Field field:fields)
System.out.println(field);
}
|
通过反射来设置属性值
public class ReflectTest {
private String strName;
private int sum;
public String strTest;
public void show(){
System.out.println(strName);
System.out.println(""+sum);
System.out.println(strTest);
}
public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
ReflectTest rTest=new ReflectTest();
Class<?> clsType = ReflectTest.class;
//获取三个属性值
Field field1=clsType.getDeclaredField("strName");
Field field2=clsType.getDeclaredField("sum");
Field field3=clsType.getField("strTest");
//反射设置对象的属性值
field1.set(rTest, "james");
field2.set(rTest, 10);
field3.set(rTest, "reflect field");
rTest.show();
}
} |
Method类
在获取Class对象后,我们还可以获取类所对应的方法,这时就要用到我们的Method类
public class ReflectTest {
private void fun() {
System.out.println("this is fun()");
}
private void add(int a, int b) {
System.out.println("the sum is: " + (a + b) + " ");
}
public static void main(String[] args) {
Class<?> clsType = ReflectTest.class;
// 返回class对象所对应的类或接口中,所声明的所有方法的数组(包括私有方法)
Method[] methods = clsType.getDeclaredMethods();
// 打印出所有的方法名
for (Method method : methods) {
System.out.println(method);
}
}
} |
再来看看怎么反射调用对象的方法
public class ReflectTest {
private void fun() {
System.out.println("this is fun()");
}
public int add(int a, int b) {
System.out.println("the sum is: " + (a + b) + " ");
return a+b;
}
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
ReflectTest rTest=new ReflectTest();
Class<?> clsType = ReflectTest.class;
// 返回对应public类型的方法,第一个参数为方法名,第二个参数为方法的参数列表
Method method=clsType.getMethod("add", new Class<?>[]{int.class,int.class});
System.out.println(""+method.invoke(rTest, new Object[]{5,6}));
}
} |
Constructor类
在获取Class对象后,我们就可以通过它创建类的对象啦。这就地用到我们的Constructor类啦。有三种创建对象的方式:
1.先获得Class对象,然后通过该Class对象的newInstance()方法直接生成即可:
Class<?> classType = StringBuffer.class;
Object obj = classType.newInstance();
2.当然也可以再获得Class对象后,通过该Class对象获得类的Constructor对象,再通过该Constructor对象的newInstance()方法创建对象:
Class<?> clsType=StringBuffer.class;
// 获得Constructor对象,此处获取一个无参数的构造方法的
Constructor<?> constructor=clsType.getConstructor(new Class<?>[]{});
// 通过Constructor对象的构造方法来生成一个对象
Object obj=constructor.newInstance(new Object[]{});
3、如果构造器带有参数,那就不能有上面两个方法来创建对象了,可使用下面这一种方式:
Class<?> clsType = StringBuffer.class;
Constructor<?> constructor = clsType.getConstructor(new Class<?>[] { String.class });
Object obj = constructor.newInstance(new Object[] { "hello, classtest" });
三. JAVA反射的原理
Class.forName(classname)的执行过程:
实际上是调用了Class类中的 Class.forName(classname, true, currentLoader)方法。参数:name - 所需类的完全限定名;initialize - 是否必须初始化类;loader - 用于加载类的类加载器。currentLoader则是通过调用ClassLoader.getCallerClassLoader()获取当前类加载器的。类要想使用,必须用类加载器加载,所以需要加载器。反射机制,不是每次都去重新反射,而是提供了cache,每次都会需要类加载器去自己的cache中查找,如果可以查到,则直接返回该类。
java的类加载器,它分为BootStrap Class Loader(引导类加载器),Extensions Class Loader (扩展类加载器),App ClassLoader(或System Class Loader),当然少不了Custom ClassLoader(用户自定义类加载器)。其加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。
流程图:
类加载器的类加载过程,先检查自己是否已经加载过该类,如果加载过,则直接返回该类,若没有则调用父类的loadClass方法,如果父类中没有,则执行findClass方法去尝试加载此类,也就是我们通常所理解的片面的"反射"了。这个过程主要通过ClassLoader.defineClass方法来完成。defineClass 方法将一个字节数组转换为 Class 类的实例(任何类的对象都是Class类的对象)。这种新定义的类的实例需要使用 Class.newInstance 来创建,而不能使用new来实例化。
在运行期间,如果我们要产生某个类的对象,Java虚拟机(JVM)会检查该类型的Class对象是否已被加载。如果没有被加载,JVM会根据类的名称找到.class文件并加载它。一旦某个类型的Class对象已被加载到内存,就可以用它来产生该类型的所有对象。
四. JAVA反射的应用-动态代理
说动态代理之前,必须先讲讲代理的设计模式。代理模式为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。代理模式的有三个角色定义:
这就需要使用我们的动态代理了。
动态代理(Dynamic Proxy)在系统运行时动态创建代理类,可以让系统能够根据实际需要来动态创建代理类,让同一个代理类能够代理多个不同的真实主题类。动态代理是一种较为高级的代理模式,它在事务管理、AOP(Aspect-OrientedProgramming,面向方面编程)等领域都发挥了重要的作用。Java语言提供了对动态代理的支持,在Java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler接口、另一个则是 Proxy类。Proxy这个类的作用就是用来动态创建一个代理对象,通常使用newProxyInstance 方法创建代理对象。Proxy代理类动态创建代理对象都需要关联到一个InvocationHandler接口,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke方法来进行调用。
动态代理的步骤
(1).创建一个实现接口InvocationHandler的类,它必须实现invoke方法
(2).创建被代理的类以及接口
(3).通过Proxy的静态方法
newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个代理
(4).通过代理调用方法
类图
1)抽象接口
public interface Subject
{ public void doSomething(String str);
}2)真实对象
public class RealSubject implements Subject
{ @Override
public void doSomething(String str)
{
// TODO Auto-generated method stub
System.out.println("do something: " + str);
}
} 3)动态代理Handler类
public class MyInvocationHandler implements InvocationHandler
{ //被代理的对象
private Object target=null;
public MyInvocationHandler(Object obj)
{
this.target=obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
// TODO Auto-generated method stub
return method.invoke(target, args);
}
} 4)场景类Client
public class Client
{ public static void main(String[] args)
{
//real object
Subject subject=new RealSubject();
//handler
InvocationHandler handler=new MyInvocationHandler(subject);
//获得代理proxy
Subject proxy=(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), handler);
System.out.println(proxy.getClass().getName());
proxy.doSomething("yes");
}
} |


