开始的时候感觉很费解,比如 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类在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类实例的方式 (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对于私有方法的限制。对于私有的属性,也可以有类似的方法。
前面是输出了Object类所有的方法,后面是具体通过invoke来调用方法所得到的结果
转载于:https://www.cnblogs.com/Goden/p/3788808.html