| — | — |
| @Override | 表示当前的方法定义将覆盖超类中的方法,如果方法拼写错误或者方法签名不匹配,编译器便会提出错误提示 |
| @Deprecated | 表示当前方法已经被弃用,如果开发者使用了注解为它的元素,编译器便会发出警告信息 |
| @SuppressWarnings | 可以关闭不当的编译器警告信息 |
所谓元注解(meta-annotation)也是一种注解,只不过这种注解负责注解其他的注解。所以再说元注解之前我们来看一下普通的注解:
public @interface LogClassMessage {
}
这是一个最普通的注解,注解的定义看起来很像一个接口,在 interface 前加上 @ 符号。事实上在语言级别上,注解也和 java 中的接口、类、枚举是同一个级别的,都会被编译成 class 文件。而前面提到的元注解存在的目的就是为了修饰这些普通注解,但是要明确一点,元注解只是给普通注解提供了作用,并不是必须存在的。
| java 提供的元注解 | 作用 |
| — | — |
| @Target | 定义你的注解应用到什么地方〔详见下文解释〕 |
| @Retention | 定义该注解在哪个级别可用〔详见下文解释〕 |
| @Documented | 将此注解包含在 javadoc 中 |
| @Inherited | 允许子类继承超类中的注解 |
〔1〕@Target使用的时候添加一个 ElementType 参数,表示当前注解可以应用到什么地方,即可以指定一种,也可以同时指定多种,使用方法如下:
//当前的注解只能应用到类、接口(包括注解)、enum上面
@Target(ElementType.TYPE)
public @interface LogClassMessage {
}
//当前的注解只能应用到方法和成员变量上面
@Target({ElementType.METHOD,ElementType.FIELD})
public @interface LogClassMessage {
}
下面来看一下 ElementType 全部的参数:
| ElementType 参数 | 说明 |
| — | — |
| ElementType.CONSTRUCTOR | 构造器的声明 |
| ElementType.FIELD | 域的声明(包括enum的实例) |
| ElementType.LOCATION_VARLABLE | 局部变量的声明 |
| ElementType.METHOD | 方法的声明 |
| ElementType.PACKAGE | 包的声明 |
| ElementType.PARAMETER | 参数的声明 |
| ElementType.TYPE | 类、接口(包括注解类型)、enum声明 |
〔2〕@Retention用来注解在哪一个级别可用,需要添加一个 RetentionPolicy 参数,用来表示在源代码中(SOURCE),在类文件中(CLASS)或者运行时(RUNTIME):
//当前注解运行时可用
@Retention(RetentionPolicy.RUNTIME)
public @interface LogClassMessage {
}
下面来看一下 RetentionPolicy 全部的参数:
| RetentionPolicy 参数 | 说明 |
| — | — |
| RetentionPolicy.SOURCE | 注解将被编译器丢弃,只能存于源代码中 |
| RetentionPolicy.CLASS | 注解在class文件中可用,能够存于编译之后的字节码之中,但会被VM丢弃 |
| RetentionPolicy.RUNTIME | VM在运行期也会保留注解,因此运行期注解可以通过反射获取注解的相关信息 |
在注解中,一般都会包含一些元素表示某些值,并且可以为这些元素设置默认值,没有元素的注解也称为标记注解(marker annotation)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.FIELD})
public @interface LogClassMessage {
public int id () default -1;
public String message() default “”;
}
注:虽然上面的 id 和 message 定义和接口的方法定义很类似,但是在注解中将 id 和 message 称为:int元素id , String 元素 message。而且注解元素的类型是有限制的,并不是任何类型都可以,主要包括:基本数据类型(理论上是没有基本类型的包装类型的,但是由于自动封装箱,所以也不会报错)、String 类型、enum 类型、Class 类型、Annotation 类型、以及以上类型的数组,(小伙伴们没有等字哦,说明目前注解的元素类型只支持上面列出的这几种),否则编译器便会提示错误。
invalid type ‘void ’ for annotation member //例如注解类型为void的错误信息
对于默认值限制 ,Bruce Eckel (美) 在其书中是这样描述的:编译器对元素的默认值有些过分挑剔,首先,元素不能有不确定的值。也就是说,元素必须要么具有默认值,要么在使用注解时提供注解的值。其次,对于非基本类型的元素,无论在源代码声明中,或者在注解接口中定义默认值时,都不能以 null 作为其值。这个约束使得处理器很难表现一个元素的存在或缺失的状态,因为在每个注解的声明中,所有元素都存在,并且都具有相应的值。为了绕开这个约束,我们只能自己定义一些特殊的值,例如空字符串或者负数,以此表示某个元素的不存在,这算得上是一个习惯用法。
怎么说呢,接触一种新的知识的途径有很多,可能每一种的结果都是大同小异的,都能让你学到东西,但是实现的方式、实现过程中的规范、方法和思路却并不一定是最佳的,本人选择的是借鉴源码,效果还不错,在这里推荐给大家。
上文讲到的是注解的基本语法,那么系统是怎么用的呢?首先让我们来看一下使用频率最高的 @Override :
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
1
2
3
4
〔1〕首先系统定义一个没有元素的标记注解 Override ,随后使用元注解 @Target 指明 Override 注解只能应用于方法之上(你可以细想想,是不是在我们实际使用这个注解的时候,只能是重写的方法,没有见过重写类或者字段的吧),使用注解 @Retention 表示当前注解只能存在源代码中,并不会出现在编译之后的 class 文件之中。
@Override protected void onResume() { super.onResume(); }
1
2
3
4
〔2〕在活动 activity 中我们可以重写 onResume() 方法,添加注解 @override 之后编译器便会去检查父类中是否存在相同方法,如果不存在便会报错。
〔3〕也许到这里你会感到很疑惑,注解到底是怎么工作的,怎么系统这样定义一个注解 Override 它就能工作了?黑魔法吗,擦擦,完成看不到实现过程嘛(泪流满面),经过查阅了一些资料(非权威)了解到,其实处理过程都编写在了编译器里面,也就是说编译器已经给我们写好了处理方法,当编译器进行检查的时候就会调用相应的处理方法。
介绍之前,先引用 Jeremy Meyer 的一段话ÿ