JAVA反射

一. 什么是JAVA反射

java反射机制是在程序运行状态中,对于任何一个类都可以知道这个类的属性和方法。对于任何一个对象都可以调用它的任意一个方法。Java反射机制运行程序判断分析任何一个类的结构,包括成员方法和变量,并调用任意一个对象的方法。java

二. JAVA反射的实例

在JDK中,主要由如下类来实现Java反射机制,这些类(除了第一个)都位于java.lang.reflect包中编程

Class类:表明一个类,位于java.lang包下。设计模式

Field类:表明类的成员变量(成员变量也称为类的属性)。api

Method类:表明类的方法。数组

Constructor类:表明类的构造方法。app

 

Class类ide

Class类是用来保存运行时类型信息的类。每一个类(型)都有一个Class对象。运行程序时,Java虚拟机(JVM)首先检查是否所要加载的类对应的Class对象是否已经加载。若是没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。通常当某个类的Class对象被载入内存时,它就能够用来建立这个类的全部对象。this

经常使用的获取Class对象的3种方式:url

1.使用Class类的静态方法。例如:  spa

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[]{});

三、若是构造器带有参数,那就不能有上面两个方法来建立对象了,可以使用下面这一种方式:

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反射的应用-动态代理

说动态代理以前,必须先讲讲代理的设计模式。代理模式为其余对象提供一种代理以控制对这个对象的访问。在某些状况下,一个客户不想或者不能直接引用另外一个对象,而代理对象能够在客户端和目标对象之间起到中介的做用。代理模式的有三个角色定义:

1)抽象主题接口(Subject)
声明真实对象和代理对象的共同接口。代理模式中,代理类和真实对象都实现了接口。
2)真实主题类(RealSubject)
真正实现业务逻辑的类。
3)代理类(Proxy)
用来代理和封装真实主题。代理对象角色内部含有对真实对象的引用,从而能够操做真实对象。同时,代理对象能够在执行真实对象操做时,附加其余的操做,至关于对真实对象进行封装。一般状况下,代理模式中的每个代理类在编译以后都会生成一个class文件,代理类所实现的接口和所代理的方法都被固定,这种代理被称之为静态代理(Static Proxy),
缺点:1. 若是须要为不一样的真实主题类提供代理类,都须要增长新的代理类,这将致使系统中的类个数急剧增长。
           2. 静态代理类和真实主题类实现了相同的接口,代理类经过真实主题类实现了相同的方法,若是接口增长一个方法,除了全部实现类须要实现这个方法外,全部代理类也须要实现此方法,增长了代码维护的复杂度。

这就须要使用咱们的动态代理了。

动态代理(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).经过代理调用方法

类图

Dynamic Proxy
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" );
  }
}
相关文章
相关标签/搜索