注解的使用有助于减少样板代码的编写,并提供了一种声明性的方法来描述代码的意图和行为。可以用于实现依赖注入,数据库映射、运行时权限处理等功能。

1. 值约束类型注解

@Nullable // 变量、参数或返回值可能为 null@NonNull // 变量、参数或返回值不能为 null@IntRange // 指定整型或长整型取值范围, eg:    void setAlpha(@IntRange(from=0, to=255) int alpha) {...}@FloatRange // 指定浮点型取值范围, eg:    void setAlpha(@FloatRange(from=0.0, to=1.0) int alpha) {...}@Size // 数组、集合或字符串的长度    @Size(min=2)  // 最小长度    @Size(max=2)  // 最大长度    @Size(2)  // 固定长度    @Size(multiple=2) // 必须为2的整数倍长度

  

2. 资源类型注解

@AnimatorRes // animator 资源类型@AnimRes // anim 资源类型@AnyRes // 任意资源类型@ArrayRes // array 资源类型@AttrRes // attr 资源类型@BoolRes // boolean 资源类型@ColorRes // color 资源类型@DimenRes // dimen 资源类型@DrawableRes // drawable 资源类型@FractionRes // fraction 资源类型@IdRes // id 资源类型@IntegerRes // integer 资源类型@InterpolatorRes // interpolator 资源类型@LayoutRes // layout 资源类型@MenuRes // menu 资源类型@PluralsRes // plurals 资源类型@RawRes // raw 资源类型@StringRes // string 资源类型@StyleableRes // styleable 资源类型@StyleRes // style 资源类型@TransitionRes // transition 资源类型@XmlRes // xml 资源类型

3. 线程类型注解

@MainThread // 主线程@UiThread // UI线程,除非不同线程显示不同UI,否则 @MainThread 与 @UiThread 无差别@WorkerThread // 工作线程,不能执行UI操作@BinderThread // 用于执行 ContentProvider 中的 query/insert/update/delete 方法@AnyThread // 任意线程

  

4. 权限类型注解@RequiresPermission

4.1 使用 @RequiresPermission 注解可以验证方法调用方是否拥有权限。

@RequiresPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)public abstract void onSaveToLocal(Bitmap bitmap) throws IOException;

  

4.3 如果是涉及多个权限,可以通过 anyOf 和 allOf 来控制判断条件:

(1) anyOf: 注解权限列表是否满足任意一个:

@RequiresPermission(anyOf = {    Manifest.permission.READ_EXTERNAL_STORAGE,    Manifest.permission.WRITE_EXTERNAL_STORAGE})public static final void readFile(String dest) {    // ...}

(2) allOf: 注解权限列表是否全部拥有:

@RequiresPermission(allOf = {    Manifest.permission.READ_EXTERNAL_STORAGE,    Manifest.permission.WRITE_EXTERNAL_STORAGE})public static final void copyFile(String dest, String source) {    // ...}

  

5. 间接权限

如果权限依赖于提供给方法参数的特定值,请对参数本身使用 @RequiresPermission,而不用列出具体权限。例如, startActivity(Intent) 方法会对传递到方法的 intent 使用间接权限:

public abstract void startCallActivity(@RequiresPermission Intent intent, @Nullable Bundle b) {...}

在您使用间接权限时,构建工具将执行数据流分析以检查传递到方法的参数是否具有任何 @RequiresPermission 注解。随后,它们会对方法本身强制参数的任何现有注解。在 startActivity(Intent) 示例中,当一个不具有相应权限的 intent 传递到方法时,Intent 类中的注解会针对 startActivity(Intent) 的无效使用生成警告,例如:

@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)@RequiresPermission(Manifest.permission.CALL_PHONE)public static final String ACTION_CALL = "android.intent.action.CALL";
public void onCall() {    Intent callIntent = new Intent(ACTION_CALL);    startCallActivity(callIntent, null);}@Overridepublic void startCallActivity(@RequiresPermission Intent intent, @Nullable Bundle b) {    intent.setData(Uri.parse("tel:" + phoneNumber));    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);    if (b != null) {        intent.setBundle(b);    }    startActivity(intent);}

  

6. Java 注解

Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。 Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。

Java内置了三种标准注解,其定义在java.lang中。

@Override // 表示当前的方法定义将覆盖超类中的方法。@Deprecated // 被此注解标记的元素表示被废弃,如果程序员使用了注解为它的元素,那么编译器会发出警告。@SuppressWarnings // 关闭不当的编译器警告信息, 常用的有:    ▪ @SuppressWarnings("unused") // 去除单类型告警    ▪ @SuppressWarnings("unused", "unchecked") // 去除多类型告警    ▪ @SuppressWarnings("all") // 去除全部类型告警

  

7. 自定义注解

定义新的 Annotation 类型使用 @interface 关键字。

public @interface MyAnnotation {    String name();  // 字符串    String password() default "abc";  // 带默认值的字符串    int age() default 18;  // 数值类型    Gender gender() default Gender.FEMALE;  // 枚举类型    Class clazz();  // 类类型    MyAnnotation2 my2();  // 注解中包含其他注解    char[] arr() default {'a', 'b', 'c'};  // 数组类型}

引用:

@MyAnnotation(name="张三", age=38, gender=Gender.MALE, class=String.class, 
my2=@MyAnnotion2(name="xxx"), arr={'1','2','3'})public void set() {...}

  

8. 元注解

元注解的作用是负责注解其他注解,主要分为6种:

@Retention  // 指定其所修饰的注解的保留策略;@Document  // 该注解是一个标记注解,用于指示一个注解将被文档化;@Target  // 用来限制注解的使用范围;@Inherited  // 该注解使父类的注解能被其子类继承;@Repeatable  // 该注解是Java8新增的注解,用于开发重复注解;类型注解(Type Annotation)  // 该注解是Java8新增的注解,可以用在任何用到类型的地方。

  

8.1 @Retention 注解

@Retention 注解用于指定被修饰的注解可以保留到JVM哪个阶段。保留策略值有以下三个:

策略值功能描述
Retention.SOURCE注解只在保留在源文件中,在编译期间删除
Retention.CLASS注解只在编译器间存在于.class文件中,运行时JVM不可获取信息,该策略值也是默认值
Retention.RUNTION可保留到JVM运行时期间,可以获取注解信息(反射),也是最长注解持续时间

【示例】保留到JVM运行时注解

@Retention(@RetentionPolocy.RUNTIME)// 定义注解

  

8.2 @Document 注解

@Document 注解用于指定被修饰的注解可以被 javadoc 工具提取成文档,定义注解类时使用。

@Document 注解进行修饰,则所有使用该注解修饰的程序元素的 API 文档中将会包含该注解说明。

@Document// 定义注解

  

8.3 Target 注解

@Target 注解用来限制注解的使用范围。

@Target({类型1, 类型2, ...})

  

枚举值功能描述
ElementType.Type可以修饰类、接口、注解或枚举类型
ElementType.FIELD可以修饰属性,包括枚举常量
ElementType.METHOD可以修饰方法
ElementType.PAPAMETER可以修饰参数
ElementType.CONSTRUCTOR可以修饰构造方法
ElementType.LOCAL_VARIABLE可以修饰局部变量
ElementType.ANNOTATION_TYPE可以修饰注解类
ElementType.PACKAGE可以修饰包
@Target(ElementType.FIELD)// 定义注解

  

8.4 @Inherited 注解

@Inherited 注解指定注解具有继承性,如果摸个注解使用 @Inherited 进行修饰,则该类使用该注解是,其子类将自动被修饰。

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Inheritedpublic @interface InheritedAnno1 {    String comment();    int order() default 0;}

  

// 使用自定义的 @MyInheritedAnno 注解修饰 Base@MyInheritedAnno(comment="继承注解", order=2)public class Base {}// InheritedDemo 只继承了 Base 类// 并被直接使用 @MyInheritedAnno 注解public class InheritedDemo extends Base {\    public InheritedDemo() {        // 从 InheritedDemo 中获取 MyInheritedAnno 注解信息        MyInheritedAnno anno = InheritedDemo.class.getAnnotation(MyInheritedAnno.class);        // 输出 MyInheritedAnno 注解成员信息        Systen.out.println(anno.comment() + ":" + anno.order());        // 打印 InheritedDemo 类是否具有 @MyInheritedAnno 修饰        System.out.println(InheritedDemo.class.isAnnotationPresent(MyInheritedAnno.class));    }}// 运行结果继承注解:2true

  

8.5 @Repeatable注解

@Repeatable 注解是 Java8 新增的注解,用于开发重复注解。在 Java8 之前,同一个程序元素前只能使用一个相同类型的注解,如果需要在同一个元素前使用多个相同类型的注解必须通过注解容器来实现。从 Java8 开始,允许使用多个相同的类型注解来修饰同一个元素,前提是该类型的注解是可重复的,即在定义注解时要用 @Repeatable 元注解进行修饰。

import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@interface AnnolContents{//该注解是容器    //定义value成员变量,该成员变量可以接受多个@RepeatableAnnol注解    RepeatableAnnol[] value();} @Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Repeatable(AnnolContents.class)public @interface RepeatableAnnol {    String name() default "南瓜仔仔";    int age();}

  

@RepeatableAnnol(age = 10)@RepeatableAnnol(name = "寻找南瓜", age = 22)public class RepeatableAnnolDemo {    public static void main(String[] args) {        //使用java8新增的getDeclaredAnnotationsByType()方法获取        //修饰类的多个@RepeatableAnnol注解        RepeatableAnnol[] annols = RepeatableAnnolDemo.class.getDeclaredAnnotationsByType(RepeatableAnnol.class);        //遍历@RepeatableAnnol注解并显示        for (RepeatableAnnol annol :annols){            System.out.println(annol.name() + "--->" + annol.age());        }        //使用传统的getDeclaredAnnotation()方法获取修饰类的@AnnolContents注解        AnnolContents container = RepeatableAnnolDemo.class.getDeclaredAnnotation(AnnolContents.class);        System.out.println(container);    }}

【运行结果:】

南瓜仔仔—>10
寻找南瓜—>22
@com.ngd.test03.AnnolContents({@com.ngd.test03.RepeatableAnnol(name=”\u5357\u74dc\u4ed4\u4ed4″, age=10), @com.ngd.test03.RepeatableAnnol(name=”\u5bfb\u627e\u5357\u74dc”, age=22)})

8.6 类型注解

Java8为ElementType枚举增加了TYPE_PARAMETER和TYPE_USE两个枚举类值,允许在定义枚举类时使用@Target(ElementType.TYPE_USE)来修饰,此种注解被称为“类型注解”(Type Annotation)。
在Java8之前,只能在定义类、接口、方法和成员变量等程序元素时使用注解,从Java8开始新增的类型注解可以用在任何用到类型的地方。
除了在定义类、接口和方法等常见的程序元素时可以使用类型注解,还可以在以下几个位置使用类型注解进行修饰:

  • 创建对象(使用new关键字创建)
  • 类型转换
  • 使用implements实现接口
  • 使用throws声明抛出异常序列
  • 方法参数
//定义一个简单的类型注解,不带任何成员变量@Target(ElementType.TYPE_USE)@interface NotNull{} //定义类时使用@NotNull类型注解@NotNullpublic class TypeAnnotationDemo implements @NotNull Serializable {//implements时使用类型注解    //方法形参中使用类型注解    public static void main(@NotNull String[] args) throws @NotNull FileNotFoundException {//throws使用时用类型注解        Object obj = "南瓜仔仔";        //强制类型转换时使用类型注解        String str = (@NotNull String)obj;        //创建对象时使用类型注解        Object win = new @NotNull JFrame("QST_Login");    }    //泛型中使用类型注解    public void foo(List info){}}

上述代码在各种情况下使用@ NotNull 类型注解,这种无处不在的类型注解可以让编译器执行更严格的代码检查,从而提高程序的健壮性。需要说明的是,上面的程序虽然大量使用了@ NotNull 类型注解,但这些注解是不会起到任何作用的,因为 Java 8本身并没有为这些类型注解提供处理工具,不能对类型注解执行检查框架。因此,如果需要类型注解发挥作用,需要程序员自己实现类型注解检查框架。目前有些第三方组织发布了类型注解检查工具,程序员可以直接使用这些第三方框架提供的检查工具,从而让编译器执行更严格的检查,以保证代码的健壮性。