zoukankan      html  css  js  c++  java
  • java反射机制初探

     最近和一位师兄交流了一下Java,真可谓是大有收获,让我好好的学习了一下javad的反射机制,同终于明白了spring等框架的一个基本实现的思想,那么今天就和大家分享一下java的反射机制。

        反射,reflection,听其名就像照镜子一样,可以看见自己也可以看见别人的每一部分。在java语言中这是一个很重要的特性。下面是来自sun公司官网关于反射的介绍: 

    Reflection is a feature in the Java programming language. It allows an executing Java program to examine or "introspect" upon itself, and manipulate internal properties of the program. For example, it's possible for a Java class to obtain the names of all its members and display them.

    The ability to examine and manipulate a Java class from within itself may not sound like very much, but in other programming languages this feature simply doesn't exist. For example, there is no way in a Pascal, C, or C++ program to obtain information about the functions defined within that program.

    One tangible use of reflection is in JavaBeans, where software components can be manipulated visually via a builder tool. The tool uses reflection to obtain the properties of Java components (classes) as they are dynamically loaded.


          那么解释一下就是,反射是java语言的一个特性,它允程序在运行时(注意不是编译的时候)来进行自我检查并且对内部的成员进行操作。例如它允许一个java的类获取他所有的成员变量和方法并且显示出来。这个能特定我们不常看到,但是在其他的比如C或者C++语言中很不就存在这个特性。一个常见的例子是在JavaBean中,一些组件可以通过一个构造器来操作。这个构造器就是用的反射在动态加载的时候来获取的java中类的属性的。


           反射的前传:类类型   Class Class                                                                                                                                     

         

       java中有一个类很特殊,就是Class类,很多朋友在写程序的时候有用过比如Apple.class来查看类型信息,大家就可以把它理解为封装了类的信息,很多解释说Class类没有构造器,其实是有的,只不过它的构造方法是private的(构造函数还有private的??有,这样是为了禁止开发者去自己创建Class类的实例)。我们可以看一下JDK中源码:

          

      注释很明确的告诉了我们,这个类是有JVM来创建的,所以我们就不用麻烦了。如果我们拿到一个类的类型信息,就可以利用反射获取其各种成员以及方法了。(注:Class 从JDK1.5版本后就开始更多为泛型服务了)那么我们怎么拿到一个类型的信息呢?假设我们有一个Role类:

    复制代码
     1 package yui;
     2 
     3 /**
     4  * A base class having some attributes and methods
     5  * @author Octobershiner
     6  * @since 2012 3 17
     7  * 
     8  * */
    10 public class Role {
    11     
    12     private String name;
    13     private String type;
    14     
    15     // Constructors
    16     public Role(){
    17         System.out.println("Constructor Role() is invoking");
    18     }
    19     //私有构造器
    20     private Role(String name){
    21         this.name = name;
    22         System.out.println("Constructor Role(String name) is invoking.");
    23     }
    24     
    25     //get and set method
    26     
    27     public String getName() {
    28         return name;
    29     }
    30     public void setName(String name) {
    31         this.name = name;
    32     }
    33     public String getType() {
    34         return type;
    35     }
    36     public void setType(String type) {
    37         this.type = type;
    38     }
    39     
    40     //override the toString method to show the class
    41     @Override
    42     public String toString(){
    43         return "This is a role called "+this.name;
    44     }
    45     
    46 }
    复制代码

      在没有对象实例的时候,主要有两种办法。

            //获得类类型的两种方式
            Class cls1 = Role.class;
            Class cls2 = Class.forName("yui.Role");

      注意第二种方式中,forName中的参数一定是完整的类名(包名+类名),并且这个方法需要捕获异常。现在得到cls1就可以创建一个Role类的实例了,利用Class的newInstance方法相当于调用类的默认的构造器

            Object o = cls1.newInstance(); //创建一个实例
            //Object o1 = new Role();   //与上面的方法等价

      这样就创建了一个对象,缺点是我们只能利用默认构造函数,因为Class的newInstance是不接受参数的,后面会讲到可接受参数的newInstance,第二,如果类的构造函数是private的,比如Class,我们仍旧不能实例化其对象。

       获取类的构造器                                                                                                                                                                       

         

      首先介绍一下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的都是获得所有的构造方法,包括私有,哈,这下我们就可以调用原本不允许调用的私有构造器了,看代码
     
    复制代码
     1         /**
     2          * 获取构造方法Constructor
     3          * getConstructor()  only for public 
     4          * getDeclaredConstructor()  global access all 
     5          * 
     6          * */
     7         
     8         //指定参数列表获取特定的方法
     9         Constructor con = cls1.getDeclaredConstructor(new Class[]{String.class});
    10         con.setAccessible(true); //设置可访问的权限
    11         Object obj = con.newInstance(new Object[]{"liyang"});
    12         System.out.println(obj);  //打印一下这个对象的信息
    13         
    14 //获取所有的构造方法集合
    15         Constructor con1[] = cls1.getDeclaredConstructors();
    16         con1[1].setAccessible(true);
    17         Object obj1 = con1[1].newInstance(new Object[]{"tom"});
    18         System.out.println(obj1);
    复制代码
      
      解释一下:第一个是获得一个指定的方法,我们指定了参数是一个String类型,第二段我们获得了所有的构造方法集合,并选取了其中一个创建了新的对象。注意这里Constructor的newInstance方法就可以设置参数了,与文章前面的同样的方法形成了对比。
      注意,以上的四个方法全部需要抛出异常,当我们获得私有的方法的时候,要用setAccessible设置一下可访问的权限,例子中没有演示获取共有方法,那个比较简单,就不做介绍了,其实掌握了上面两个,其他就好理解了。
     
    获取类的成员变量                                                                                                                                                                    
         
       了解了构造器,其实你可以猜到成员变量的获取方法了,成员变量用Field类进行封装。
           主要的方法非常的类似:
    • public Field getDeclaredField(String name)  获取任意指定名字的成员
    • public Field[] getDeclaredFields()             获取所有的成员变量
    • public Field getField(String name)           获取任意public成员变量
    • public Field[] getFields()                          获取所有的public成员变量
        可以看出这些方法都是异曲同工的,好了直接看一下例子吧
     
    复制代码
    1         /**
    2          * 获取成员变量Field
    3          * getField()
    4          * getDeclaredField()
    5          * */
    6         Field mem = cls1.getDeclaredField("name");
    7         mem.setAccessible(true);      
    8         System.out.println("we get form field :"+mem.get(obj));
    9          
    复制代码
      
      这是在访问私有变量,什么私有变量也可以访问??是的。。。。
         
          获取类的方法                                                                                                                                                                      
         
       我觉得你已经可以帮我写这一段了,封装类的方法的类是Method.获取method也有四个方法,猜到了没??
    • public Method[] getMethods()    获取所有的共有方法的集合
    • public Method getMethod(String name,Class<?>... parameterTypes) 获取指定公有方法 参数1:方法名 参数2:参数类型集合  
    • public Method[] getDeclaredMethods()  获取所有的方法
    • public Method getDeclaredMethod(String name,Class<?>... parameterTypes) 获取任意指定方法
           看下面的例子吧
       
    复制代码
    1         /**
    2          * 调用类的方法 Method
    3          * getMethod()
    4          * getDeclaredMethod()
    5          * 
    6          * */
    7         Method f = cls1.getMethod("getName", null);
    8         Object name = f.invoke(obj, null);
    9         System.out.println("we invoke method : "+ name);
    复制代码
             
      这个很简单吧,无参的时候我们只要传null就行了。

    总结:                                                                                                                                                                                      

           以上就是反射机制的简单的使用,显然学过spring的朋友一定明白了,为什么可以通过配置文件就可以让我们获得指定的方法和变量,在我们创建对象的时候都是通过传进string实现的,就好像你需要什么,我们去为你生产,还有我们一直在用Object,这就说明java语言的动态特性,依赖性大大的降低了。
  • 相关阅读:
    智能移动机器人背后蕴含的技术——激光雷达
    Kalman Filters
    Fiddler抓HttpClient的包
    VSCode开发WebApi EFCore的坑
    WPF之小米Logo超圆角的实现
    windows react打包发布
    jenkins in docker踩坑汇总
    Using ML.NET in Jupyter notebooks 在jupyter notebook中使用ML.NET ——No design time or full build available
    【Linux知识点】CentOS7 更换阿里云源
    【Golang 报错】exec gcc executable file not found in %PATH%
  • 原文地址:https://www.cnblogs.com/Code-Engineering/p/5745414.html
Copyright © 2011-2022 走看看