全面说说反射机制

来源:互联网 发布:centos iscsi target 编辑:程序博客网 时间:2024/06/18 17:40

1 反射机制的基本概念



        JAVA反射机制是在运行状态中,对于任意一个,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。


2 反射是如何实现的


1.简单介绍Class

                      ——java基于此基础,才能够实现反射


        Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。

        也就是说,ClassLoader找到了需要调用的类时(java为了调控内存的调用消耗,类的加载都在需要时再进行,很抠但是很有效),就会加载它,然后根据.class文件内记载的类信息来产生一个与该类相联系的独一无二的Class对象。Class对象记载了该类的字段,方法等等信息。以后jvm要产生该类的实例,就是根据内存中存在的该Class类所记载的信息(Class对象应该和我所了解的其他类一样会在堆内存内产生、消亡)来进行。

         而java中的Class类对象是可以人工自然性的(也就是说开放的)得到的(虽然你无法像其他类一样运用构造器来得到它的实例,因为Class对象都是jvm产生的。不过话说回来,客户产生的话也是无意义的),而且,更伟大的是,基于这个基础,java实现了反射机制

          我的理解:在JVM中,根据.class文件加载后,产生了独一无二的Class对象(这个是由JVM创建的,根据

图中所示,Class内部的私有构造器),而后如果还要产生该类的实例,就根据刚才生成的Class对象,通过newInstance得到,但是不能利用构造器得到实例了。


2.如何做到反射?


          反射其实就是一个文本扫描器,扫描.class文件。


3Reflection动态相关机制


3.1动态语言


         Reflection是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过ReflectionAPIs取得任何一个已知名称的class的内部信息。

包括其modifiers(诸如publicstatic等)、superclass(例如Object)、实现了的 interfaces (例如Serializable)、也包括其fieldsmethods的所有信息,并可于运行时改变fields内容或调用methods

         换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。

这种“看透”class的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。

3.2Java反射机制提供的功能


1.在运行时判断任意一个对象所属的类。

2.在运行时构造任意一个类的对象。

3.在运行时判断任意一个类所具有的成员变量和方法。

4.在运行时调用任意一个对象的方法。

 

3.3Java Reflection API 简介和使用


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


  • Class类:代表一个类,位于java.lang包下。
  • Field类:代表类的成员变量(成员变量也称为类的属性)。
  • Method类:代表类的方法。
  • Constructor类:代表类的构造方法。
  • Array类:提供了动态创建数组,以及访问数组的元素的静态方法。


3.3.1Class对象


         要想使用反射,首先需要获得待操作的类所对应的Class对象。

         Java中,无论生成某个类的多少个对象,这些对象都会对应于同一个Class对象。

         这个Class对象是由JVM生成的,通过它能够获悉整个类的结构


获取Class对象的3种方式(以User类为例):


  1. 使用Class类的静态方法,如:

Class<?> clazz = Class.forName("com.bobo.User");


         2.使用类的.class语法,如

Class clazz = User.class;


          3.使用对象的getClass()方法,如

User user =new User();

Class<?> clazz =user.getClass();


注:getClass()方法定义在Object类中,不是静态方法,需要通过对象来调用,并且它声明为final,表明不能被子类所覆写。


3.3.2生成对象


          若想通过类的不带参数的构造方法来生成对象,我们有两种方式:


1.先获得Class对象,然后通过该Class对象的newInstance()方法直接生成即可:

    Class<?> classType = User.class;

    Object obj = classType.newInstance();


2.先获得Class对象,然后通过该对象获得对应的Constructor对象,再通过该Constructor对象的newInstance()方法生成 (其中User是一个自定义的类,有一个无参数的构造方法,也有带参数的构造方法):

Class<?> classType = User.class;

    // 获得Constructor对象,此处获取第一个无参数的构造方法的

    Constructor cons = classType.getConstructor(new Class[] {});

    // 通过构造方法来生成一个对象

    Object obj = cons.newInstance(new Object[] {});


若想通过类的带参数的构造方法生成对象,只能使用下面这一种方式:

(User为一个自定义的类,有无参数的构造方法,也有一个带参数的构造方法,传入字符串和整型)

    Class<?> classType = User.class;

    Constructor cons2 = classType.getConstructor(new Class[] {String.class, int.class});

    Object obj2 = cons2.newInstance(new Object[] {"ZhangSan",20});


小结: 可以看出调用构造方法生成对象的方法和调用一般方法的类似,不同的是从Class对象获取Constructor对象时不需要指定名字,而获取Method对象时需要指定名字。


3.3.3获取类的构造器


        首先介绍一下Constructor类,这个类用来封装反射得到的构造器,Class有四个方法来获得Constructor对象


    public Constructor<?>[] getConstructors()      返回类中所有的public构造器集合,默认构造器的下标为0

    public Constructor<T> getConstructor(Class<?>... parameterTypes)   返回指定public构造器,参数为构造器参数类型集合

    public Constructor<?>[] getDeclaredConstructors()  返回类中所有的构造器,包括私有

    public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回任意指定的构造器


        从名字来看,还是很好懂的,带上Declared的都是获得所有的构造方法,包括私有,哈,这下我们就可以调用原本不允许调用的私有构造器了,看代码。


例:


    //指定参数列表获取特定的方法

    Constructor con = cls1.getDeclaredConstructor(new Class[]{String.class});

    con.setAccessible(true); //设置可访问的权限

    Object obj = con.newInstance(new Object[]{"liyang"});

    System.out.println(obj);  //打印一下这个对象的信息

     

//获取所有的构造方法集合

    Constructor con1[] = cls1.getDeclaredConstructors();

    con1[1].setAccessible(true);

    Object obj1 = con1[1].newInstance(new Object[]{"tom"});

    System.out.println(obj1);

 

3.3.4获取类的成员变量


         了解了构造器,其实你可以猜到成员变量的获取方法了,成员变量用Field类进行封装。


    主要的方法非常的类似:

    public Field getDeclaredField(String name)  获取任意指定名字的成员

    public Field[] getDeclaredFields()             获取所有的成员变量

    public Field getField(String name)           获取任意public成员变量

    public Field[] getFields()                          获取所有的public成员变量


例:

     Field mem = cls1.getDeclaredField("name");

     mem.setAccessible(true);      // 设置可访问的权限

   System.out.println("we get form field :"+mem.get(obj));

 

3.3.5获取类的方法


          封装类的方法的类是Method.获取method也有四个方法:


    public Method[] getMethods()    获取所有的共有方法的集合

    public Method getMethod(String name,Class<?>... parameterTypes) 获取指定公有方法 参数1:方法名 参数2:参数类型集合 

    public Method[] getDeclaredMethods()  获取所有的方法

    public Method getDeclaredMethod(String name,Class<?>... parameterTypes) 获取任意指定方法


例:

     Method f = clazz.getMethod("getName", null);

     Object name = f.invoke(obj, null);

     System.out.println("we invoke method : "+ name);


这个很简单吧,无参的时候我们只要传null就行了。


4反射的实际应用


配置文件:

        通过配置文件就可以让我们获得指定的方法和变量,在我们创建对象的时候都是通过传进string实现的,就好像你需要什么,我们去为你生产,还有我们一直在用Object,这就说明java语言的动态特性,依赖性大大的降低了。


         AOP,Struts就是应用了这个。


5反射的优缺点


5.1优点


          反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中它的灵活性就表现的十分明显。


5.2缺点


          对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。


0 0
原创粉丝点击