理解Java的注解和反射

一、基本概念

在Java5.0定义了注解,它提供了一种为程序元素设置元数据的方法,不能直接干扰程序代码的运行,无论增加或者删除注解代码都能够正常运行。

它的作用主要体现在以下几个方面:

•  编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】

•  代码分析:通过代码里标识的元数据对代码进行分析【使用反射】

•  编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】

二、注解分类

根据注解参数的个数,我们可以将注解分为三类:

• 标记注解:一个没有成员定义的Annotation类型被称为标记注解。这种Annotation类型仅使用自身的存在与否来为我们提供信息。比如后面的系统注解@Override;

• 单值注解

• 完整注解  

根据注解使用方法和用途,我们可以将Annotation分为三类:

• JDK内置系统注解

   常见的内置系统注解有: @Override 表示此方法覆盖了父类方法; @Deprecated  表示修饰已经过时的方法; @SuppressWarnings 用于通知java编译器禁止特定的编译警告

• 元注解

元注解的作用就是负责注解其他注解,包括4个标准的meta-annotation类型,分别如下:

  1. @Target 

    Target的作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)。

    常见的取值有:

      CONSTRUCTOR:用于描述构造器

      FIELD:用于描述域

      LOCAL_VARIABLE:用于描述局部变量

      METHOD:用于描述方法

      PACKAGE:用于描述

      PARAMETER:用于描述参数

      TYPE:用于描述类、接口(包括注解类型) 或enum声明

        2. @Retention

            表示需要在什么级别保存该注释信息,具体有:

              SOURCE: 注释在源码的时候检查,在编译器丢弃

              CLASS: 注释在Class文件中可用,但是会被VM丢弃

              RUNTIME: VM将在运行时保留注释,此方法主要是通过反射机制读取注释的信息

        3.  @Documented

             将注释包含在JavaDoc中

        4. @Inherited

    允许子类继承父类的注释

 • 自定义注解

使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。

  定义注解格式: public @interface 注解名 {定义体}

  注解参数的可支持数据类型:

  1).所有基本数据类型(int,float,boolean,byte,double,char,long,short)     

       2).String类型     

       3).Class类型     

       4).enum类型     

       5).Annotation类型     

       6).以上所有类型的数组

一个自定义的注解例子:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface DatabaseField {
	
	FieldType Type() default FieldType.VARCHAR;
	
	boolean primaryKey() default false;

	boolean isLongType() default false;//用来区分int 型和 long型
	
	String fieldName() default "";
	
	enum FieldType{
		VARCHAR,INT,REAL
	}
}

 三、Java的反射机制

正常Java代码是在编译期间被加载然后再运行,但是在注解中有自定义注解和和反射机制结合起来使得代码在运行期间再进行加载运行。

Java反射的作用:

  在运行时判断任意一个对象所属的类;

  在运行时构造任意一个类的对象;

  在运行时判断任意一个类所具有的成员变量和方法;

  在运行时调用任意一个对象的方法。

通过Class类获取成员变量、成员方法、接口、超类、构造方法等 在java.lang.Object 类中定义了getClass()方法,因此对于任意一个Java对象,都可以通过此方法获得对象的类型。Class类是Reflection API 中的核心类,它有以下方法

获取class对象

        通过已知的类名来获取对应的class对象

    String className = ... ;//在运行期获取的类名字符串
    Class class = Class.forName(className);

 获取类名:

  getName():获得类的完整名字,包含包名。

Class aClass = ... //获取Class对象,具体方式可见Class对象小节
String className = aClass.getName();

如果你仅仅只是想获取类的名字(不包含包名),那么你可以使用 getSimpleName()方法:

Class aClass = ... //获取Class对象,具体方式可见Class对象小节
String simpleClassName = aClass.getSimpleName();

通过 Class 对象你可以访问类的父类或者接口

Class  aClass = ... //获取Class对象,具体方式可见Class对象小节
Class superclass = aClass.getSuperclass();
Class[] interfaces = aClass.getInterfaces();

 获取变量:

  getFields():获得类的public类型的属性变量集合,这里也包括父类中的public变量,当知道变量名称时,可以访问指定的变量,如果给定的名字错了则跑出异常。

Class aClass = ...//获取Class对象
Field[] methods = aClass.getFields();
Field field = aClass.getField("someField");

  getDeclaredFields():获得当前类的所有属性包括private。

       获取对象后可获得其变量类型,并且可以get/set变量值

Class  aClass = MyObject.class
Field field = aClass.getField("someField");
Object fieldType = field.getType();  //获得一个变量的类型

MyObject objectInstance = new MyObject();
Object value = field.get(objectInstance); //获取该类的对象中该变量的值
field.set(objetInstance, value); //设置该类的对象中该变量的值

     在对私有变量设置值的时候需要调用setAccessible()方法来关闭指定类的field实例的反射访问检查

public class PrivateObject {
  private String privateString = null;
  public PrivateObject(String privateString) {
    this.privateString = privateString;
  }
}
PrivateObject privateObject = new PrivateObject("The Private Value");
Field privateStringField = PrivateObject.class.
            getDeclaredField("privateString"); //获取该类的private对象
privateStringField.setAccessible(true); //关闭private对象的访问检查
String fieldValue = (String) privateStringField.get(privateObject); //获取该private的值

 获取方法:
  getMethods():获得类的public类型的方法。

Class aClass = ...//获取Class对象
Method[] methods = aClass.getMethods();

通过method对象来调用一个方法,使用Method.invoke(Object target, Object ... parameters), 第一个参数是指该类对象实例,如果该方法为静态方法则第一个参数可为null,后续的参数则需要根据函数定义的参数一一对应

//获取一个static方法名为doSomesthing,参数类型为String的方法
Method method = MyObject.class.getMethod("doSomething", String.class);
Object returnValue = method.invoke(null, "parameter-value1");

       getDeclaredMethods():获得类的所有方法包括私有方法。

  getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。

 获取类的构造方法:

  getConstructors():获得类的public类型的构造方法。

  getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。

  newInstance():通过类的不带参数的构造方法创建这个类的一个对象。

获取注解类:

//获取class对象的所有注解
Annotation[] annotations = (Annotation[]) class1.getAnnotations();
//获取class对象指定注解
Annotation annotation = (Annotation) class1.getAnnotation(Deprecated.class);

posted on 2017-06-20 10:21  kma  阅读(185)  评论(0编辑  收藏  举报

导航