JAVA反射
一. 什么是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" ); } } |