zoukankan      html  css  js  c++  java
  • JAVA(五)反射机制/Annotation

    成鹏致远 | lcw.cnblog.com |2014-02-04

    反射机制

    1.认识Class类

    1. 在正常情况下,必须知道一个类的完整路径之后才可以实例化对象,但是在 java中也允许通过一个对象来找到其所在的类的信息,那么这实际上就是 Class类的功能
    2. 此时,所有的操作都是反着来的
    3. Object类的支持
      1. 在Object类中定义了以下的方法,此方法将被所有子类继承:public final Class getClass()
      2. 以上的方法返回值的类型是一个“Class”类,实际上此类是 Java反射的源头,实际上所谓反射就是:可以通过对象反射求出类的名称
    4. Class类
      1. Class本身表示一个类的本身,通过 Class可以完整的得到一个类中的完整结构,包括此类中的方法定义,属性定义等
      2. 此类在 JDK文档中没有发现任何的构造方法,所以此类的构造方法被私有化了
    5. 实例化 Class类对象,方法有三种
      1. 第一种:通过 forName()方法
      2. 第二种:类.class
      3. 第三种:对象.getClass()
    6. 一旦可以实例化 Class类之后,就可以进行反射的进一步操作
    7. 小结
      1. 了解 Class类的作用:反射的源头
      2. 三个 Class类的实例化方式,其中要以“forName()”重点掌握,类.class

    2.Class类的作用

    1. Class主要是反射的源头,不光可以取得对象所在类的信息,也可以直接通过 Class类的方法进行对象的实例化操作,正常情况下,使用关键字 new为对象实例化,如果现在已经实例化好了 Class对象,则就可以通过 Class类中提供的 newInstance()实例化对象
    2. 即使不使用关键字 new对象也可以进行实例化操作,反射的作用
    3. 【注意】在操作中类中必须存在无参构造方法,否则无法实例化
    4. 一般使用反射实例化对象的时候,类中都最好存在一个无参构造,这样的操作比较合理
    5. 如果要想调用有参构造,则必须按照以下的步骤进行:
      1. 通过 Class类中的 getConstructors()取得本类中的全部构造方法
      2. 向构造方法中传递一个对象数组进去,里面包含了构造方法中所需的各个参数
      3. 之后通过 Constructor实例化对象
    6. 小结
      1. 本节的功能是 Class用的最多的功能,而且在开发中用户也会经常使用到的开发模式
      2. 在使用 Class实例化对象的时候,必须保证类中存在一个无参构造,否则无法使用

    3.反射应用:取得类的结构

    1. 通过反射得到一个类的完整结构,需要使用到 java.lang.reflect包中的几个类
      1. Constructor:表示类中的构造方法
      2. Field:表示类中的属性
      3. Method:表示类中的方法
    2. 取得一个类所实现的全部接口
      1. 使用 Class类中的 getInterfaces()方法
      2. 此方法返回一个 Class类的对象数组,之后就可以直接利用 Class类中的 getName()方法输出即可
    3. 取得类所继承的父类
      1. 一个类可以实现多个接口,但是只能继承一个父类
      2. 如果要想取得一个类的父类,可以直接使用 Class类中的 getSuperclass()方法
      3. 此方法返回的是 Class实例,和之前得到的接口一样,可以通过 敷衍Name()方法取得名称
    4. Constructor类中的几个方法
      1. 取得修饰符:public int getModifiers()
      2. 取得方法名称:public String getName()
      3. 取得参数的类型:public Class<?>[] getParameterTypes()
    5. 还原修饰符:直接使用 Modifier类中的 public static String toString(int mod)
    6. 取得类中的方法
      1. 要想取得一个类的全部方法,可以使用 Class类中的 getDeclaredMethods()方法
      2. 此方法返回一个 Method类的对象数组,而如果要想进一步取得方法的具体信息,例如:方法的参数,抛出的异常声明等等,则就必须依靠 Method类
      3. 输出本类中的全部方法:public Method[] getDeclaredMethods() throws SecurityException
      4. 输出全部的方法:public Method[] getMethods() throws SecurityException
      5. 取得全部的返回值:public Class<?> getReturnType()
      6. 取得全部的参数:public Class<?> getParameterTypes()
      7. 取得修饰符:public int getModifiers()
      8. 取得异常信息:public Class<?>[] getExceptionTypes()
    7. 在一般的开发工具中经常看见随笔提示功能,实际上此功能就是利用以上函数完成的
    8. 取得类中的属性
      1. 得到实现的接口或父类中的公共属性:public Field[] getFields() throws SecurityException
      2. 得到本类中的全部属性:public Field[] getDeclaredFields() throws SecurityException
      3. 以上方法返回的都是 Field的数组,每一个 Field对象就表示类中的一个属性
    9. 小结
      1. 在实际的操作中,取得类的信息的操作代码用户并不会经常开发
      2. 一定要熟悉 java.lang.reflect包的作用,反射机制
      3. 如何取得属性、方法、构造的名称,修饰符等等

    4.Java反射机制深入研究

    1. 通过反射调用类中的方法
      1. 在正常情况下一个类的对象产生之后,就可以直接调用类中的方法了,如果要想调用的话,则肯定必须清楚的知道要调用的方法名称是什么,之后通过 Class类中的 public Method getMethod(String name, Class<?>... parameterTypes)
      2. 得到一个方法的 Method对象是,之后通过此 Method对象来执行方法(invoke()),但是在方法调用的时候,因为会牵扯到方法中的参数的问题,所以通过 getMethod()取得的时候,必须设置需要的参数类型
      3. 执行的时候还需要传递参数进去,而且还需要实例化对象
    2. 通过反射调用类 setter及 getter
      1. setter及 getter方法是一个标准的属性的访问方法,如果一个类的属性被封装,则必须通过 setter及 getter方法设置和取得
      2. 实际上此方法的操作之所以要这样规定,主要原因是由于反射机制可以给予支持的
    3. 通过反射调用属性
      1. 如果要操作一个类中的属性,则可以通过 Filed完成,而不必麻烦的通过 setter及 getter
      2. 在访问私有属性的时候,必须让这个属性可见:public void setAccessible(boolean flag) throws SecurityException,将其内容设置成 true即可
      3. 此操作调用与 setter及 getter无关,但是为了保证程序的安全性,最好还是通过 setter及 getter方法完成调用
    4. 通过反射操作数组
      1. 反射机制不光只能用在类中,也可以应用在任意的引用数据类型上,当然这就包含了数组,数组使用 Array类完成
      2. Class类中存在一个方法:public Class<?> getComponentType()
      3. 得到数组指定下标的内容:get(Object array, int index)
      4. 修改内容:set(Object array ,int index)
      5. 开辟新数组:newInstance(Class<?> componentType, int... dimensions)
    5. 小结
      1. 重点理解 setter及 getter方法的调用问题,原理,代码最好可以清楚的掌握
      2. 使用反射操作属性是不建议直接使用的

    5.动态代理

    1. 代理设计:一个操作的接口有两个子类,其中一个是真实主题的实现类,另外一个是代理类,代理实现类要完成比真实主题实现类更多的内容,而且本身还需要处理一些与具体业务有关的程序代码
    2. 之前写过的代理实际上称为静态代理,因为一个代理类只能为一个接口服务,那么如果现在有很多个接口的话,那么则肯定代理类就很多了;而且所有的代理操作除了调用的方法不一样之外,其它的操作都一样,则此时肯定是重复的代码
    3. InvocationHandler接口:public interface InvocationHandler{public Object invoke(Object proxy, Method method, Object[] args) throws Throwable}
      1. Object proxy:被代理的对象
      2. Method method:要调用的方法
      3. Object args[]:方法调用时所需要的参数
    4. 可以将 InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉 ProxySubject
    5. Proxy类
      1. Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类
      2. public static Object newProxyInstance(ClassLoader loader, Class<?>[] interface, InvocationHandler h) throws IIIlegalArgumentException
        1. ClassLoader loader:类加载器
        2. Class<?>[] interfaces:得到全部的接口
        3. InvocationHandler h:得到 InvocationHandler接口的子类实例
    6. 类加载器
      1. 在 Proxy类中的 newProxyInstance()方法中需要一个 ClassLoader类的实例,ClassLoader实际上对应的是类加载器
      2. 在Java中主要有以下三种类加载器
        1. Bootstrap ClassLoader:此加载器采用 C++编写,一般开发中是看不到的
        2. Extension ClassLoader:用来进行扩展类的加载,一般对应的是 jrelibext目录中的类
        3. AppClassLoader:加载 classpath指定的类,是最常使用的一种加载器
      3. 如果要想得到一个加载器的对象,则肯定使用 class类完成
    7. 小结
      1. 使用动态代理,可以完成代理的功能,而且可以代理全部的接口

    6.高级工厂设计模式

    1. 工厂设计模式最大的好处是可以在应用进行解耦合操作
    2. 之前写过的工厂设计模式存在的问题:如果想扩充一个子类,则肯定要修改工厂类,如果希望在扩充子类时不用修改工厂类的话,则就必须使用反射完成
    3. 如果直接使用反射,则需要输入完整的“包.类”名称,肯定很麻烦,所以,此时可以通过一些配置文件的方式保存这些完整的类路径
      1. 所以此时可以通过属性文件的形式配置所要的子类信息
    4. 程序运行的时候,就可以通过属性文件的内容读取出来,之后直接操作属性文件的 key,就可以避免输入过长的类路径
    5. 配置文件与程序代码相分离,这种设计思路是以后开发基本思路,当然,最新的设计理念:是在程序中直接通过注释的方式进行配置
    6. 小结
      1. 反射机制对程序开发所带来的好处
      2. 程序代码与配置文件相分离的理论

    Annotation

    1.系统内建Annotation

    1. Annotation:在 JDK1.5之后增加的一个新特性,这种特性被称为元数据特性,在 JDK1.5之后称为注释,即:使用注释的方式加入一些程序的信息
    2. java.lang.annotation.Annotation接口是所有的 Annotation都必须实现的接口
    3. 在 JDK1.5之后,系统中已经建立了如下的三个内建的 Annotation类型,用户直接使用即可
      1. @Override:覆写的 Annotation
      2. @Deprecated:不赞成使用的 Annotation
      3. @SuppressWarnings:压制安全警告的 Annotation
    4. @Override:表示方法覆写的正确性,使用 @Override注释可以保证程序正确的执行
    5. @Deprecated:使用 @Deprecated注释的 Annotation本身是不建议使用的一个操作
    6. @SuppressWarnings:用于压制警告信息
      1. 可以压制一个警告信息,也可以同时压制多个警告信息,只需要以数组的形式出现即可
      2. 在设置注释信息的时候,是以 key->value的形式出现的,所以 @SuppressWarnings也可以直接使用“value={"unchecked","deprecation"}”的方式设置
    7. 小结
      1. 系统内建的三个 Annotation的作用,可以发现通过注释可以完成一些代码的其它功能

    2.自定义Annotation

    1. 定义简单的 Annotation:[public] @interface Annotation 名称{数据类型 变量名称();}
    2. 之后就可以直接在程序中使用“@Annotation 名称”的格式了
    3. 还可以向 Annotation中设置参数,使用变量接收参数
      1. 在使用时就必须清楚的指定变量的内容
      2. 也可以使用明确的标记,表明内容赋给哪个参数
      3. 也可以设置多个参数
      4. 还可以设置一个数组进去
      5. 以上全部的 Annotation中有一个特点,所有的参数内容需要在使用注释的时候设置上去,那么也可以为一个参数设置默认的内容,在声明的时候使用 default即可,当再去使用此 Annotation的时候就可以不用设置内容
    4. 在操作中,对于一个 Annotation而言,有时候会固定其取值范围,只能取固定的几个值,那么这个时候实际上就需要依靠枚举,那么外部使用此 Annotation的时候也需要从枚举固定取值
    5. Retention和RetentionPolicy
      1. 在 Annotation,可以使用 Retention定义一个 Annotation的保存范围
      2. Retention定义中存在了一个 RetentionPolicy的变量,此变量用于指定 Annotation的保存范围,RetentionPolicy包含三种范围
        1. 在三个范围中,最需要关心的就是 RUNTIME范围,因为在执行时候起作用,在以后反射与 Annotation的操作中,也必须使用此种范围
    6. 内建 Annotation的 RetentionPolicy
      1. 三个内建的 Annotation
      2. Override:定义采用的是 @Retention(value=SOURCE),只能在源文件中出现
      3. Deprecated:定义采用的是 @Retention(value=RUNTIME),可以在执行时出现
      4. SuppressWarnings:定义采用的是 @Retention(value=SOURCE),只能在源文件中出现
    7. 小结
      1. Annotation定义格式,及变量的类型:枚举、数组、普通变量
      2. Retention、RetentionPolicy的作用

    3.反射与 Annotation

    1. 一个 Annotation如果要想让其变得有意义,则必须结合反射机制取得 Annotation中设置的全部内容
    2. 系统内建的三个 Annotation,只有 Deprecated的 Annotation的定义范围是 RUNTIME范围
    3. 在 Class类中存在与 Annotation操作有关的方法
      1. 如果在一个元素中存在注释,则取得全部注释:getAnnotation()
      2. 判断一个元素上是否存在注释:isAnnotationPresent()
    4. 小结
      1. Annotation在实际的开发中,不管如何使用,其最终肯定是结合反射机制
      2. 也就是说可以通过 Annotation设置一些内容到一个方法上去,以完成特定的功能

    4.深入Annotation

    1. 之前定义的 Annotation,如果没有明确的说明可以在任意的位置上使用
    2. 如果现在需要指定其使用的范围的话,则就必须使用 @Target注释
    3. ElementType的保存范围
    4. 如果希望一个 Annotation可以在类及方法上同时使用的话,则必须设置多个范围
    5. @Documented:可以在任何的 Annotation上使用
      1. 所有的 Annotation默认情况下都是使用 @Document进行注释的
      2. 生成的 javadoc的时候可以通过 @Document设置一些说明信息
    6. @Inherited:此注释表示一个 Annotation是否可以被继承下来
    7. 如果在父类上使用 @Inherited,则其子类可以将此 Annotation继承下来
    8. 小结
      1. 熟悉 Documented注释的作用:加入说明信息
      2. 熟悉 Target的作用,并使用 Target注释指定注释的使用位置
      3. 如果一个 Annotation要想被子类继承下来则使用 Inherited注释说明
      4. 反射机制对于操作 Annotation是最重要的
     
  • 相关阅读:
    转贴:CSS伪类与CSS伪元素的区别及由来具体说明
    Docker + Consul 多数据中心模拟
    Spring之事件发布系统
    ExecutorService的submit方法的坑
    固定频率调用接口方案
    叠罗汉III之推箱子
    叠罗汉II
    叠罗汉I
    滑雪
    华为OJ:火车进站
  • 原文地址:https://www.cnblogs.com/lcw/p/3540973.html
Copyright © 2011-2022 走看看