JAVA反射

 

 java代码在计算机中经历的三个阶段

第一个阶段(源代码阶段):

先从java文件按成员变量,构造方法,成员方法通过javac编译成class文件。如上图Person.java-->Person.class

第二个阶段(Class类对象阶段):

通过类加载器把class文件中的成员变量,构造方法,成员方法加载到内存中。

第三个阶段(Runtime运行时阶段):

该阶段就可以创建对象和调用对象里的方法了。

 

注意:同一个字节码文件(*.class) 在一次程序运行过程中,只会被加载一次,不论通过何种方式获取。

获取Class对象的方式

1.Class.forName("全类名"):将字节码文件加载到内存,返回Class对象。

  * 多用于配置文件,将类名定义在配置文件中。读取文件加载类。

2.类名.class:通过类名的属性class获取。

  * 多用于参数的传递

3.对象.getClass():getClass()方法在Object类中定义着。

  *多用于对象的获取字节码的方式

第一种和第二种相比较,第二种更具优势:

  1.代码更安全,程序在编译阶段就可以检查需要访问的Class对象是否存在。

  2.程序性能更高,因为这种方式无须调用方法,所以性能更好。

但如果我们只有一个字符串, 例如“java.lang. String”,如果需要获取该字符串对应的Class对象,则只能使用第二种方
式了,使用Class的forName方法获取Class对象时,该方法可能抛出一个ClasNotFoundException异常

 

从Class中获取信息
Class类提供了大量实例方法来获取该Class对象所对应类的详细信息,Class类大致包含如下几种方法,下面每种方法都可能包括多个重载的版本。
四个方法用于访问Class对应的类所包含的构造器:
  • Constructor<T> getConstructor( Class<?> parameterTypes):返回此Class对象所表示的类的指定的public构造器。
  • Constructor<?>[] getConstructor():返回此Class对象所表示的类的所有public构造器。
  • Constructor<T> getDeclaredConstructor(Class<?>...parameterTypes):返回此Class对象所表示的类的指定构造器,与构造器的访问级别无关。
  • Constructor<?>[] getDeclaaredConstructirs(): 返回此Class对象所表示的类的所有构造器,与构造器的访问级别无关。
 
四个方法用于访问Class对应的类所包含的方法:
  • Method getMethod(String name,Class<?>...parameterTypes): 返回此Class对象所表示的类的指定public方法。
  • Method[] getMethods(): 返回此Class对象所表示的类的所有public方法。
  • Method getDeclaredMethod(String name,Class<?>...parameterTypes):返回此Class对象所表示的类的指定方法,与方法的访问级别无关。
  • Method[] getDeclaredMethods(): 返回此Class对象所表示的类的全部方法,与方法的访问级别无关。
 
如下四个方法用于访问Class对应的类所包含的属性(Field):
  • Field getField(String name): 返回此Class对象所表示的类的指定public 属性(Field)。
  • Field[] getFields(): 返回此Class对象所表示的类的所有public属性(Field)。
  • Field getDeclaredField(String name):返回此Class对象所表示的类的指定属性(Field),与属性的访问级别无关。
  • Field[] getDeclaredFields():返回此Class对象所表示的类的全部属性(Field),与属性的访问级别无关。
 
如下三个用于访问Class对应的类上所包含的注释:
  • <A extends Annotation> A getAnnotation(Class<A> annotationClass): 试图获取该Class对象所表示类上指定类型的注释:如果该类型不存在则返回null。
  • Annotation[] getAnnotations():返回此元素上存在的所有注释。
  • Annotation[] getDeclaredAnnottations():返回直接存在于此元素上的所有注释。
 
如下方法用于访问该Class对象对应类包含的内部类:
  • Class<?>[] getDeclaredClasses():返回该Class对象所对应类里包含的全部内部类。
如下方法用于访问该Class对象对应类所在的外部类:
  • Class<?>getDeclaredClass():返回该Class对应所赌赢类所在的外部类。
 
如下方法用于访问该Class对应所对应类所继承的父类、所实现的接口等:
  • Class<?>[] getInterfaces():返回该Class对象对应类所实现的全部接口。
  • int getModifiers():返回此类或接口的所有修饰符。修饰符由public、protected、private、final、static、abstract等对应的常量组成,返回的整数应使用Modifier工具类的方法来解码,才可以获取真实的修饰符。
  • Package getPackage():获取此类的包。
  • String getName(): 以字符串形式返回此Class对象所表示的类的名称。
  • String getSimpleName():以字符串形式返回此Class对象所表示的类的简称。
  • Class<? super T> getSuperclass():返回该Class所表示的类的超类对应的Class对象。
 
除此之外,Class对象还可调用如下几个判断方法来判断该类是否为接口、枚举、注释类型等:
  • boolean isAnnotation():返回此Class对象是否表示一个注释类型(由@interface定义)。
  • boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):判断此Class对象上是否使用了Annotation注释修饰。
  • boolean isAnonymousClass():返回此Class对象是否是一个匿名类。
  • boolean isArray():返回此Class对象是否表示一个数组类。
  • boolean isEnum():返回此Class对象是否表示一个枚举(由enum关键字定义)。
  • boolean isInterface():返回此Class对象是否表示一个接口(使用interface定义)。
  • boolean isInstance(Object obj):判断obj是否是此Class对象的实例,该方法可以完全代替instanceof操作符。

 

使用反射生成并操作对象
Class对象可以获得该类里的成分包括方法(由Method对象标识)、构造器(由Constructor对象表示)、Field(由Field对象表示),这三个类都定义在java.lang.reflect包下,并实现了java.lang.reflect.Member接口,程序可以通过Method对象来执行对应的方法,通关Constructor对象来调用对应的构造器创建对象,能通过Field对象直接访问并修改对象的属性值。
 
创建对象
通过反射来生成对象有如下两种方式:
  • 使用Class对象的newInstance()方法来创建该Class对象对应类的实例,这种方式要求该Class对象的对应类有默认构造器,而执行newInstance()方法时实际上是利用默认构造器来创建该类的实例。
  • 先使用Class对象来获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例。通过这种方式可以选择使用某个类的指定构造器来创建实例。
 
如果我们不想利用默认构造器来创建JAVA对象,而想利用指定的构造器来创建JAVA对象,则需要利用Constructor对象了,每个Constructor对应一个构造器。为了利用指定构造器来创建JAVA对象需要如下三个步骤:
  • 获取该类的class对象。
  • 利用Class对象的getConstructor(参数是默认构造器的对象.class)方法来获取指定构造器。
  • 利用Constructor的newInstance()方法来创建JAVA对象。
 
调用方法
当获得某个类对应的Class对象后,就可以通过该Class对象的getMethods()方法或getMethod()方法来获取全部方法或指定方法---这两个方法的返回值是Method对象数组,或者Method对象。
每个Method对象对应一个方法,获得Method对象后,程序就可通过该Method来调用对应方法。在Method里包含一个invoke方法,该方法的签名如下:
  • Object invoke(Object obj,Object...args):该方法中的obj是执行该方法的主调,后面的args是执行该方法时传入该方法的实参。
当通过Method的invoke方法来调用对应的方法时,JAVA会要求程序必须有调用该方法的权限。如果程序确实需要调用某个对象的private方法,可以先调用Method对象的如下方法:
  • setAccessible(boolean flag):将Method对象的accessible标志设置为指示的布尔值。值为true则指示该Method在使用时应该取消JAVA语言访问权限检查。值为false则指示该Method在使用时应该实施JAVA语言访问权限检查。
 
 
访问属性值
通过Class对象的getFields()或getField()方法可以获取该类所包括的全部Field(属性)或指定Field。Field提供了如下两组方法来访问属性:
  • getXxx(Object obj):获取obj对象该Field的属性值。此处的Xxx对应8个基本类型,如果该属性的类型时引用类型则取消get后面的Xxx。
  • setXxx(Object obj,Xxx val): 将obj对象的该Field设置成val值。此处Xxxx对应8个基本类型,如果该属性的类型是引用类型则取消set后面的Xxx。
  • 使用这两个方法可以随意的访问指定对象的所有属性,包括private访问控制的属性。
 
操作数组
在java.lang.reflect包下还提供了一个Array类,Array对象可以代表所有的数组。程序可以通过使用Array来动态地创建数组,操作数组元素等。
Array提供了如下几类方法:
  • static Object newInstance(Class<?> componentType, int ....length):创建一个具有指定的元素类型、指定维护的新数组。
  • static xxx getXxx(Object array,int index):返回array数组中第index个元素。其中xxx是各种基本数据类型,如果数组元素时引用类型,则该方法变为get(Object array,int index)。
  • static void setXxx(Object array,int index,xxx val):将array数组中第index元素的值设为val。其中xxx是各种基本数据类型,如果数组元素是引用类型,则该方法变成set(Object array, int index, Object val).
 
使用反射生成JDK动态代理
在Java的 java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口通过使用这个类和接口可以生成JDK动态代理类或动态代理对象。
 
使用Proxy 和 InvocationHandler创建动态代理
Proxy提供用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类。如果我们再程序中为一个或多个接口动态地生成实现类,就可以使用Proxy来创建动态代理类;如果需要为一个或多个接口动态地创建实例,也可以使用Proxy来创建动态代理实例。
Proxy提供了如下两个方法来创建动态代理类和动态代理实例:
  • static Class<?> getProxyClass(ClassLoader loader,Class<?>...interfaces):创建一个动态代理类所对应的class对象,该代理类将实现interfaces所指定的多个接口。第一个ClassLoader指定生成动态代理类的加载器。
  • static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h):直接创建一个动态代理对象,该代理对象的实现类实现了interfaces指定的系列接口,执行代理对象的每个方法时都会被替代执行InvocationHandler对象的invoke方法。
实际上,即使采用第一种方式获取了一个动态代理类之后,当程序需要通过该代理类来创建对象时一样需要传入一个InvocationHandler对象,也就是说,系统生成的每个代理对象都有一个与之关联的InvocationHandler对象。

posted @ 2021-01-10 13:17  year12  阅读(102)  评论(0编辑  收藏  举报