JAVA(五)反射机制/Annotation
成鹏致远 | lcw.cnblog.com |2014-02-04
反射机制
1.认识Class类
- 在正常情况下,必须知道一个类的完整路径之后才可以实例化对象,但是在 java中也允许通过一个对象来找到其所在的类的信息,那么这实际上就是 Class类的功能
- 此时,所有的操作都是反着来的
- Object类的支持
- 在Object类中定义了以下的方法,此方法将被所有子类继承:public final Class getClass()
- 以上的方法返回值的类型是一个“Class”类,实际上此类是 Java反射的源头,实际上所谓反射就是:可以通过对象反射求出类的名称
- Class类
- Class本身表示一个类的本身,通过 Class可以完整的得到一个类中的完整结构,包括此类中的方法定义,属性定义等
- 此类在 JDK文档中没有发现任何的构造方法,所以此类的构造方法被私有化了
- 实例化 Class类对象,方法有三种
- 第一种:通过 forName()方法
- 第二种:类.class
- 第三种:对象.getClass()
- 一旦可以实例化 Class类之后,就可以进行反射的进一步操作
- 小结
- 了解 Class类的作用:反射的源头
- 三个 Class类的实例化方式,其中要以“forName()”重点掌握,类.class
2.Class类的作用
- Class主要是反射的源头,不光可以取得对象所在类的信息,也可以直接通过 Class类的方法进行对象的实例化操作,正常情况下,使用关键字 new为对象实例化,如果现在已经实例化好了 Class对象,则就可以通过 Class类中提供的 newInstance()实例化对象
- 即使不使用关键字 new对象也可以进行实例化操作,反射的作用
- 【注意】在操作中类中必须存在无参构造方法,否则无法实例化
- 一般使用反射实例化对象的时候,类中都最好存在一个无参构造,这样的操作比较合理
- 如果要想调用有参构造,则必须按照以下的步骤进行:
- 通过 Class类中的 getConstructors()取得本类中的全部构造方法
- 向构造方法中传递一个对象数组进去,里面包含了构造方法中所需的各个参数
- 之后通过 Constructor实例化对象
- 小结
- 本节的功能是 Class用的最多的功能,而且在开发中用户也会经常使用到的开发模式
- 在使用 Class实例化对象的时候,必须保证类中存在一个无参构造,否则无法使用
3.反射应用:取得类的结构
- 通过反射得到一个类的完整结构,需要使用到 java.lang.reflect包中的几个类
- Constructor:表示类中的构造方法
- Field:表示类中的属性
- Method:表示类中的方法
- 取得一个类所实现的全部接口
- 使用 Class类中的 getInterfaces()方法
- 此方法返回一个 Class类的对象数组,之后就可以直接利用 Class类中的 getName()方法输出即可
- 取得类所继承的父类
- 一个类可以实现多个接口,但是只能继承一个父类
- 如果要想取得一个类的父类,可以直接使用 Class类中的 getSuperclass()方法
- 此方法返回的是 Class实例,和之前得到的接口一样,可以通过 敷衍Name()方法取得名称
- Constructor类中的几个方法
- 取得修饰符:public int getModifiers()
- 取得方法名称:public String getName()
- 取得参数的类型:public Class<?>[] getParameterTypes()
- 还原修饰符:直接使用 Modifier类中的 public static String toString(int mod)
- 取得类中的方法
- 要想取得一个类的全部方法,可以使用 Class类中的 getDeclaredMethods()方法
- 此方法返回一个 Method类的对象数组,而如果要想进一步取得方法的具体信息,例如:方法的参数,抛出的异常声明等等,则就必须依靠 Method类
- 输出本类中的全部方法:public Method[] getDeclaredMethods() throws SecurityException
- 输出全部的方法:public Method[] getMethods() throws SecurityException
- 取得全部的返回值:public Class<?> getReturnType()
- 取得全部的参数:public Class<?> getParameterTypes()
- 取得修饰符:public int getModifiers()
- 取得异常信息:public Class<?>[] getExceptionTypes()
- 在一般的开发工具中经常看见随笔提示功能,实际上此功能就是利用以上函数完成的
- 取得类中的属性
- 得到实现的接口或父类中的公共属性:public Field[] getFields() throws SecurityException
- 得到本类中的全部属性:public Field[] getDeclaredFields() throws SecurityException
- 以上方法返回的都是 Field的数组,每一个 Field对象就表示类中的一个属性
- 小结
- 在实际的操作中,取得类的信息的操作代码用户并不会经常开发
- 一定要熟悉 java.lang.reflect包的作用,反射机制
- 如何取得属性、方法、构造的名称,修饰符等等
4.Java反射机制深入研究
- 通过反射调用类中的方法
- 在正常情况下一个类的对象产生之后,就可以直接调用类中的方法了,如果要想调用的话,则肯定必须清楚的知道要调用的方法名称是什么,之后通过 Class类中的 public Method getMethod(String name, Class<?>... parameterTypes)
- 得到一个方法的 Method对象是,之后通过此 Method对象来执行方法(invoke()),但是在方法调用的时候,因为会牵扯到方法中的参数的问题,所以通过 getMethod()取得的时候,必须设置需要的参数类型
- 执行的时候还需要传递参数进去,而且还需要实例化对象
- 通过反射调用类 setter及 getter
- setter及 getter方法是一个标准的属性的访问方法,如果一个类的属性被封装,则必须通过 setter及 getter方法设置和取得
- 实际上此方法的操作之所以要这样规定,主要原因是由于反射机制可以给予支持的
- 通过反射调用属性
- 如果要操作一个类中的属性,则可以通过 Filed完成,而不必麻烦的通过 setter及 getter
- 在访问私有属性的时候,必须让这个属性可见:public void setAccessible(boolean flag) throws SecurityException,将其内容设置成 true即可
- 此操作调用与 setter及 getter无关,但是为了保证程序的安全性,最好还是通过 setter及 getter方法完成调用
- 通过反射操作数组
- 反射机制不光只能用在类中,也可以应用在任意的引用数据类型上,当然这就包含了数组,数组使用 Array类完成
- Class类中存在一个方法:public Class<?> getComponentType()
- 得到数组指定下标的内容:get(Object array, int index)
- 修改内容:set(Object array ,int index)
- 开辟新数组:newInstance(Class<?> componentType, int... dimensions)
- 小结
- 重点理解 setter及 getter方法的调用问题,原理,代码最好可以清楚的掌握
- 使用反射操作属性是不建议直接使用的
5.动态代理
- 代理设计:一个操作的接口有两个子类,其中一个是真实主题的实现类,另外一个是代理类,代理实现类要完成比真实主题实现类更多的内容,而且本身还需要处理一些与具体业务有关的程序代码
- 之前写过的代理实际上称为静态代理,因为一个代理类只能为一个接口服务,那么如果现在有很多个接口的话,那么则肯定代理类就很多了;而且所有的代理操作除了调用的方法不一样之外,其它的操作都一样,则此时肯定是重复的代码
- InvocationHandler接口:public interface InvocationHandler{public Object invoke(Object proxy, Method method, Object[] args) throws Throwable}
- Object proxy:被代理的对象
- Method method:要调用的方法
- Object args[]:方法调用时所需要的参数
- 可以将 InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉 ProxySubject
- Proxy类
- Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类
- public static Object newProxyInstance(ClassLoader loader, Class<?>[] interface, InvocationHandler h) throws IIIlegalArgumentException
- ClassLoader loader:类加载器
- Class<?>[] interfaces:得到全部的接口
- InvocationHandler h:得到 InvocationHandler接口的子类实例
- 类加载器
- 在 Proxy类中的 newProxyInstance()方法中需要一个 ClassLoader类的实例,ClassLoader实际上对应的是类加载器
- 在Java中主要有以下三种类加载器
- Bootstrap ClassLoader:此加载器采用 C++编写,一般开发中是看不到的
- Extension ClassLoader:用来进行扩展类的加载,一般对应的是 jre\lib\ext目录中的类
- AppClassLoader:加载 classpath指定的类,是最常使用的一种加载器
- 如果要想得到一个加载器的对象,则肯定使用 class类完成
- 小结
- 使用动态代理,可以完成代理的功能,而且可以代理全部的接口
6.高级工厂设计模式
- 工厂设计模式最大的好处是可以在应用进行解耦合操作
- 之前写过的工厂设计模式存在的问题:如果想扩充一个子类,则肯定要修改工厂类,如果希望在扩充子类时不用修改工厂类的话,则就必须使用反射完成
- 如果直接使用反射,则需要输入完整的“包.类”名称,肯定很麻烦,所以,此时可以通过一些配置文件的方式保存这些完整的类路径
- 所以此时可以通过属性文件的形式配置所要的子类信息
- 程序运行的时候,就可以通过属性文件的内容读取出来,之后直接操作属性文件的 key,就可以避免输入过长的类路径
- 配置文件与程序代码相分离,这种设计思路是以后开发基本思路,当然,最新的设计理念:是在程序中直接通过注释的方式进行配置
- 小结
- 反射机制对程序开发所带来的好处
- 程序代码与配置文件相分离的理论
Annotation
1.系统内建Annotation
- Annotation:在 JDK1.5之后增加的一个新特性,这种特性被称为元数据特性,在 JDK1.5之后称为注释,即:使用注释的方式加入一些程序的信息
- java.lang.annotation.Annotation接口是所有的 Annotation都必须实现的接口
- 在 JDK1.5之后,系统中已经建立了如下的三个内建的 Annotation类型,用户直接使用即可
- @Override:覆写的 Annotation
- @Deprecated:不赞成使用的 Annotation
- @SuppressWarnings:压制安全警告的 Annotation
- @Override:表示方法覆写的正确性,使用 @Override注释可以保证程序正确的执行
- @Deprecated:使用 @Deprecated注释的 Annotation本身是不建议使用的一个操作
- @SuppressWarnings:用于压制警告信息
- 可以压制一个警告信息,也可以同时压制多个警告信息,只需要以数组的形式出现即可
- 在设置注释信息的时候,是以 key->value的形式出现的,所以 @SuppressWarnings也可以直接使用“value={"unchecked","deprecation"}”的方式设置
- 小结
- 系统内建的三个 Annotation的作用,可以发现通过注释可以完成一些代码的其它功能
2.自定义Annotation
- 定义简单的 Annotation:[public] @interface Annotation 名称{数据类型 变量名称();}
- 之后就可以直接在程序中使用“@Annotation 名称”的格式了
- 还可以向 Annotation中设置参数,使用变量接收参数
- 在使用时就必须清楚的指定变量的内容
- 也可以使用明确的标记,表明内容赋给哪个参数
- 也可以设置多个参数
- 还可以设置一个数组进去
- 以上全部的 Annotation中有一个特点,所有的参数内容需要在使用注释的时候设置上去,那么也可以为一个参数设置默认的内容,在声明的时候使用 default即可,当再去使用此 Annotation的时候就可以不用设置内容
- 在操作中,对于一个 Annotation而言,有时候会固定其取值范围,只能取固定的几个值,那么这个时候实际上就需要依靠枚举,那么外部使用此 Annotation的时候也需要从枚举固定取值
- Retention和RetentionPolicy
- 在 Annotation,可以使用 Retention定义一个 Annotation的保存范围
- Retention定义中存在了一个 RetentionPolicy的变量,此变量用于指定 Annotation的保存范围,RetentionPolicy包含三种范围
- 在三个范围中,最需要关心的就是 RUNTIME范围,因为在执行时候起作用,在以后反射与 Annotation的操作中,也必须使用此种范围
- 内建 Annotation的 RetentionPolicy
- 三个内建的 Annotation
- Override:定义采用的是 @Retention(value=SOURCE),只能在源文件中出现
- Deprecated:定义采用的是 @Retention(value=RUNTIME),可以在执行时出现
- SuppressWarnings:定义采用的是 @Retention(value=SOURCE),只能在源文件中出现
- 小结
- Annotation定义格式,及变量的类型:枚举、数组、普通变量
- Retention、RetentionPolicy的作用
3.反射与 Annotation
- 一个 Annotation如果要想让其变得有意义,则必须结合反射机制取得 Annotation中设置的全部内容
- 系统内建的三个 Annotation,只有 Deprecated的 Annotation的定义范围是 RUNTIME范围
- 在 Class类中存在与 Annotation操作有关的方法
- 如果在一个元素中存在注释,则取得全部注释:getAnnotation()
- 判断一个元素上是否存在注释:isAnnotationPresent()
- 小结
- Annotation在实际的开发中,不管如何使用,其最终肯定是结合反射机制
- 也就是说可以通过 Annotation设置一些内容到一个方法上去,以完成特定的功能
4.深入Annotation
- 之前定义的 Annotation,如果没有明确的说明可以在任意的位置上使用
- 如果现在需要指定其使用的范围的话,则就必须使用 @Target注释
- ElementType的保存范围
- 如果希望一个 Annotation可以在类及方法上同时使用的话,则必须设置多个范围
- @Documented:可以在任何的 Annotation上使用
- 所有的 Annotation默认情况下都是使用 @Document进行注释的
- 生成的 javadoc的时候可以通过 @Document设置一些说明信息
- @Inherited:此注释表示一个 Annotation是否可以被继承下来
- 如果在父类上使用 @Inherited,则其子类可以将此 Annotation继承下来
- 小结
- 熟悉 Documented注释的作用:加入说明信息
- 熟悉 Target的作用,并使用 Target注释指定注释的使用位置
- 如果一个 Annotation要想被子类继承下来则使用 Inherited注释说明
- 反射机制对于操作 Annotation是最重要的