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:用来进行扩展类的加载,一般对应的是 jre\lib\ext目录中的类
      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是最重要的
 

 

posted @ 2014-02-08 19:59  Leo.cheng  阅读(1244)  评论(0编辑  收藏  举报