zoukankan      html  css  js  c++  java
  • java反射机制(1)

    关于Class类

    开始的时候感觉很费解,比如 Class<?> classType = Student.getClass();

    Class不是声明一个类的关键字的吗?

    好比说我们定义一个Students类,然后生成一个实例 Student stu=new Student();此时可以把Student的定义看成是一个模板,之后通过这个模板生成一个stu实例。在这个基础上在抽象一下,我们自己所书写的Students模板,也可以把它抽象成一种实例,假如说叫做模板实例吧,哪我们自己再定义一个Person的类,就相当于又生成了一个模板的实例,总之感觉上是一种更高程度的抽象,在这个层次上的模板,实际上可以看做是更高一层次的实例。因此Class类就被当做是一种特殊的类来处理了。只是理解上,实际从原理上貌似是与JVM对于class加载的机制有关,没加载一个.class文件,就有一个Class类与之相对应,比如加载一个Student.class,系统中就对应有了一个Student类,这个类实际上是Class类的一个实例,我们可以利用这个Class实例对象(即Student类)再去声明新的实例stu1,stu2之类的,目前只是理解到这个程度。

     

    除了Class类还有一些特殊的类

     

    Class类在java.lang包中

    Field Method Constructor Array几个类在java.lang.reflect包中

    刚才的那个“模板”也是一种特殊的类勉强还说的过去,那么现在Method,Array这些都算是类了,这要怎么说?具体要详细解释可能就要看源码了,我自己也不怎么清楚,以后再慢慢研究,总之目前还不影响使用。只是大致理解成和某一个类的某一个方法相关联的一个类,Constructor是和某个构造方法相关联的类,Field就是和某个变量相关联的类,Array就是和某个数组相关联的类。

    关于反射机制

    举一个例子,比如我们国家研制运载火箭,那按照通常的想法,最初肯定是先要对火箭进行设计,外形怎么样,内部结构如何,发动机怎么弄……工程师们设计好后,把火箭的一整套设计图纸交给具体负责火箭制造的部门进行研制,根据图纸可以生产处不一样用途的火箭,比如登月的火箭燃料多一些,发送卫星的火箭燃料少一些,对火箭的配置局部调整之后可以产出不同的火箭。

    这就相当于 先定义一个Rocket类 Rocket rocket1=new Rocket(p1),Rocket rocket2=new Rocket(p2);

    一次偶然的机会(的确很偶然当然这不是重点),我们国家得到了美国的一个更加高级的运载火箭,我国的工程师想通过研究火箭上的先进技术,生产出适合我国使用的拥有同样先进技术的火箭,由于没有图纸,无疑增大了难度,工程师要通过研究火箭实体,反向推导出设计图纸,在通过设计图纸,创造出新的高级的满足不同需要的火箭

    这大概在一定程度上可以帮助理解反射机制:我们可以通过反射机制,获取运行期间这个对象所属的类的相关信息。

    当然反射机制的功能不仅局限于此,通过反射机制我们可以

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

    2运行时,构造任意一个类的对象

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

    4运行时,调用任意一个对象的方法(运行之前可能还不知道这个对象是哪个类的,动态先获取类,再找到类的方法进行调用)

    可以看到,运行期间,是一个很关键的描述,虽然java属于静态语言,但是java的反射机制是一个十分突出的动态相关的机制,我们可以于运行的时候,加载,探知,使用在编译期间完全未知的classes。java程序可以加载一个运行时才得名的class 获取其完整的结构。并且可以通过获取到的类的信息在进行其他相关的操作。

    Java中的好多框架都用到了反射机制,比如使用很广泛的动态代理机制。

     

    Class类与Method类的常用方法

    获取class类实例的方式 (Class类的实例可以说是反射的入口 只有获得了一个类的Class类对象 才能利用反射机制的API来进一步分析)

    1使用class类的静态方法

    Class<?> classType=Class.forname(“某一个类名”)

    2通过一个类的自有.class方法 返回一个类的实例

    Class<?> classType=String.class

    用class类生成新的对象的方式

    3使用对象的getClass方法(最常用到的 相当于前面例子中的通过一个火箭 逆推出火箭设计的图纸)

    String s=”abc”

    Class<?> classType=s.getClass();

    通过class类与method类调用某个类中的方法

    即使是比较复杂的反射程序,在这一块也主要是按照以下三个大步骤类

    1 获得class类的实例(主要是由上面的三种方式构成)

    2 通过一个class类的实例classType获得一个classType类的对象

    Object obj=classType.newInstance()

    但是这种方式只能是对象初始化的时候没有参数才能这样用,相当于对象的构造函数的参数列表为空的时候才能这样用。

    如果一个对象的构造函数需要一些初始化参数,比如需要传入两个int值作为初始化时候的参数那该怎么办?
    这个时候就要先获得Constructor类的实例,这个类是与一个类的构造函数相关联的

    //注意这个时候getClass()函数中的形参,仅仅是对于传入的初始化的参数的描述信息,即是

    //说初始化参数都是什么类,并不是具体的值,仅仅是定性的描述

    Constructor cons=classType.getConstructor(new Class{int.class,int.class });

    //通过cons实例来对一个新的classType实例进行初始化

    //由于是运行期间生成的 先前并不知道是哪种类 所以用Object来声明

    //此时传入的参数数组要是实际的值

    Object obj=cons.newInstance(new Object{1,2})

    3 通过Method类来调用方法

    比如我们的classType类里面有一个名为add方法,其形参是两个int 变量

    //注意这里只是填描述性的信息,对于方法的形参的描述,通常用一个Class类数组来表示

    Method addmethod=classType.getMethod(“add”,new Class[]{int.class,int.class})

    //Class类还有一个getMethods的方法,可以获得对应Class类的所有的public方法,包括从父类中继承的方法,所有的方法会放在一个Method类的数组中,在JUnit单元测试框架中会用到这个

    //这里会执行invoke方法 进行具体的调用

    //这里是进行方法具体调用的过程 主要含义是说在obj实例上调用add方法,传入的形参

    //分别是obj[0]=1 obj[1]=2 由于不知道具体是什么类型 这里用obj数组类来表示传入的形参

    //返回值是add方法的返回值

    Object obj[]=addMethod.invoke(obj,new Object[]{1,2})

    其实这个与上面的constructor生成实例的方法还是比较像的,因为构造函数也是一种特殊的方法,只不过方法名与类名相同,所以就不需要在额外指定在那个实例上运行了,就是省去了第一个参数

    也许会有这样的疑惑,既然在第二步里面已经生成了obj实例,那直接obj.add(1,2)这样调用方法不就行了吗,干嘛要绕一大圈,还要通过什么method方法。

    事实上动态代理机制的核心就是在这个地方,由于Method生成的具体的实例可以是一个接口,比如不同类里面对于add接口有不同的实现,所以invoke第一个参数不管传进来什么类,都可以动态的调用这个类中所对应的add方法,这样就有种以不变应万变的感觉,在某些情况下可以使得程序更加简洁,具体可以参考动态代理模式。

    访问私有方法和属性 

    反射有一个强大的地方就是可以破坏对象的封装性,在某些框架中可能会用得到,就像留了一个后门一样,突破了private的封锁。

    Class有getMethod与getDeclaredMethod两个方法,这里还是要特别注意一下getxxx 与getdeclaredxxx 的区别,getxxx一般是获得所有public类型的方法,包括从父类中继承过来的,而getdeclaredxxx方法主要是获得的是自身类的所有方法,包括public以及private类型的,不会受到访问权限的限制。因此使用第二个声明的方式可以得到私有的方法,仅仅这样是不够的,在通过method实例调用它的invoke方法之前,还需要加上method.setAccessible(true),这个setAccessible是Method的父类java.lang.reflect.Accessible类的方法,可以通过设置setAcessible(true)来越过private对于私有方法的限制。对于私有的属性,也可以有类似的方法。

    一个简单的示例程序

    package com.javase.reflection;
    
    public class invokeTest {
        
        public void addMethod(int a,int b){
            System.out.println("invoke addMethod the result is "+(int)(a+b));
        }
    
        public void echoMethod(String str){
            System.out.println(str);
        }
    }
    
    package com.javase.reflection;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class reflectionTest {
        public static void main(String[]args) throws Exception{
            //利用forname加载一个类 由于对应的类可能没有找到 必须要加上异常
            //classType是一个类对象 表示某个类(模板)的实例
            Class<?> classTypea=Class.forName(Object.class.getName());
            //返回类型为classType的类(模版)中所定义的全部方法
            Method[] methodsa=classTypea.getDeclaredMethods();
            
            for(Method method:methodsa){
                System.out.println(method);
            }
            
            Class<?> classTypeb=Class.forName(reflectionTest.class.getName());
            Method[] methodsb=classTypeb.getDeclaredMethods();
            
            for(Method method:methodsb){
                System.out.println(method.getName());
            }
            
            //另一种生成类的方式
            Class<?> classTypeinvoke=invokeTest.class;
            //这里用classTypea classTypeb 也都是可以的
            Object invoketest=classTypeinvoke.newInstance();
            
            //传统的方式就是直接通过reflectiontest实例来调用addMethod 和echoMethod
            //有时候则需要通过放射的方式来进行调用
            
            //方法的类型对象.getMethod(方法的名称,方法的形参的类型对象所组成的Class数组)
            //返回一个Method对象
            Method addmethod=classTypeinvoke.getMethod("addMethod", new Class[]{int.class,int.class});
            //通过哪个方法生成了这个Method对象 就调用哪个方法
            //在invoketest这个实例的基础上 调用这个方法 出传入的实际参数是 new Object[]{1,2} 注意要声明成object类型
            addmethod.invoke(invoketest, new Object[]{1,2});
            
            Method echomethod=classTypeinvoke.getMethod("echoMethod", new Class[]{String.class});
            echomethod.invoke(invoketest, new Object[]{"invoke echomethod"});
        }
    }
    
    
    运行结果:
    protected void java.lang.Object.finalize() throws java.lang.Throwable
    public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
    public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
    public final void java.lang.Object.wait() throws java.lang.InterruptedException
    public boolean java.lang.Object.equals(java.lang.Object)
    public java.lang.String java.lang.Object.toString()
    public native int java.lang.Object.hashCode()
    public final native java.lang.Class java.lang.Object.getClass()
    protected native java.lang.Object java.lang.Object.clone() throws java.lang.CloneNotSupportedException
    private static native void java.lang.Object.registerNatives()
    public final native void java.lang.Object.notify()
    public final native void java.lang.Object.notifyAll()
    main
    invoke addMethod the result is 3
    invoke echomethod
    

      

    前面是输出了Object类所有的方法,后面是具体通过invoke来调用方法所得到的结果

  • 相关阅读:
    MySQL模糊匹配查询like、regexp、in
    MySQL数据库——表操作
    (转)MySQL join语法解析与性能分析
    (转)解决 TortoiseGit 诡异的 Bad file number 问题
    四、windows下TortoiseGit的使用与操作
    (转)创建GitHub技术博客
    二、Windows下TortoiseGit的安装与配置
    三、TortoiseGit之配置密钥
    集训之6-26模拟赛一
    集训之各种dp
  • 原文地址:https://www.cnblogs.com/Goden/p/3788808.html
Copyright © 2011-2022 走看看