Java注解

来源:互联网 发布:淘宝客推广网站怎么建 编辑:程序博客网 时间:2024/06/05 02:04

Java最难的部分不是语言本身,而是其过多的库和各种框架,而现在各种框架中,基本上是无“注解“不欢,注解已成为必须非常熟练掌握的一部分了。Java 1.8进一步增加了注解的一些新特性。

什么是注解

Annotations, a form of metadata, provide data about a program that is not part of the program itself. Annotations have no direct effect on the operation of the code they annotate.

先贴上它的官方定义:注解不是程序自身的一部分,其为程序提供元数据支持,注解对它所注解的代码没有直接的影响。

注解的作用

注解可以用来:

  • 为编译器提供信息:帮助编译器检查代码是否错误或禁止编译器发出警告信息,我们常用的@Override和@SuppressWarnings就属于这种,对应于RetentionPolicy.SOURCE。
  • 编译和部署时处理:一些软件工具能够根据注解信息生成代码、XML文件等等,对应于RetentionPolicy.CLASS。
  • 运行时处理:在代码运行时检查注解,对应于RetentionPolicy.RUNTIME。

注解可以使用的地方

  1. 用作声明:可以用在在类、字段、方法和其他程序的元素中;
  2. Java 1.8后,也可以用在类型上,如下面的示例(下面仅仅是示例说明,这些注解需要自己定义):

    • 类实例创建表达式:
     new @Interned MyObject();
  • 类型转换
     myString = (@NonNull String) str;
  • implements 语句
     class UnmodifiableList<T> implements        @Readonly List<@Readonly T> { ... }
  • 抛异常的语句
        void monitorTemperature() throws        @Critical TemperatureException { ... }

Java预定义的注解类型

Java自身内置了一些注解类型,这些注解一些编译器提供检查信息的,另外一些是用来定义注解时使用的

编译器使用的注解

原来只有三个注解类型,并且全部在 java.lang包中:@Deprecated, @Override, 和 @SuppressWarnings,在java 1.7中,增加了@SafeVarargs,java 1.8中又增加了@FunctionalInterface

@Deprecated

用 @Deprecated 注释的程序元素,标志着所标注的这个元素已被弃用,不鼓励程序员再使用这样的元素,通常是因为它很危险或存在更好的选择。在使用不被赞成的程序元素或在不被赞成的代码中执行重写时,编译器会发出警告。
当用@Deprecated 标注时,最好在Javadoc中也要标志出来,不过要注意的是,Javadoc使用的是@deprecated,大小写不同。

   // Javadoc comment follows    /**     * @deprecated     * explanation of why it was deprecated     */    @Deprecated    static void deprecatedMethod() { }

@Override

表示一个方法声明打算重写超类中的另一个方法声明。如果方法利用此注释类型进行注解但没有重写超类方法,编译器会不让你通过编译的。

@Override    int overriddenMethod() { }

@SuppressWarnings

指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告。

   @SuppressWarnings("deprecation")   void useDeprecatedMethod(View view) {        ......        // deprecation warning        // - suppressed        view.setBackgroundDrawable(background);    }

每个编译器警告属于一个类别。 Java语言规范列出了两个类别:deprecation和unchecked,而且这两个可以同时使用:

    @SuppressWarnings({"unchecked", "deprecation"})

@SafeVarargs

用于指明那些使用可变长度参数的方法和构造器是安全的。这些方法能被传入长度可变的参数。这些参数可以是泛型的。如果它们是泛型参数,使用@SafeVarargs注释就可以抑制那些烦人的警告信息。

@FunctionalInterface

表明这是一个函数式接口,要弄清楚函数式接口的定义需要先弄明白Lambda表达式,所谓的函数式接口,当然首先是一个接口,然后就是在这个接口里面只能有一个抽象方法。这种类型的接口也称为SAM接口,即Single Abstract Method interfaces。

既然是接口了,其所修饰的类型只能是interface了。

    @FunctionalInterface    interface GreetingService     {        void sayMessage(String message);    }

定义其他注解时用到的注解

这种注解通常被称为元注解,也就是注解的最小单元,它们在java.lang.annotation中定义,原来有4个:@Retention,@Documented,@Target和@Inherited,Java 1.8又引入了两个:@Native和@Repeatable。

@Retention

标志了注解的生命周期,如果注释类型声明中不存在 Retention 注释,则保留策略默认为 RetentionPolicy.CLASS:

  • RetentionPolicy.SOURCE: 其生命周期仅在源代码级别保留,编译时编译器就将它抛弃到了,在class文件中不会存在,为什么会有这种注解呢?那为什么会有@SuppressWarnings,@SuppressWarnings就是这种类型的注解,不需要编译就能给用户一些提示信息,另外android中的注解@NonNull也是这种,它标志某个值不应该为Null,如果为null了,IDE会给提示(IDE需要安装了检查这个语法的工具,Androidstudio已经有了),但你非要设为null它也没法。
  • RetentionPolicy.CLASS – 这种注解会被编译到class文件中,但是会被JVM忽略掉,就是说在java运行时中并不存在这种注解,这种注解见到的地方相对较少些,用在非常规的地方,常用在通过读取class的字节码加载class,而不是通过classload直接加载class的地方。
  • RetentionPolicy.RUNTIME – 其生命周期一直延续到JVM中,能够在运行时的时候通过反射机制调用到。最常见的一种注解。

@Target

定义一个注解常常包含两个元注解,一个就是刚刚说的@Retention,另一个就是@Target,前者标志了注解的生命周期,后者标志了注解能用在什么位置。

@Target标志能够用在java的哪种元素上,如是形参上,字段生命上还是方法生等等,如果不显示标注,默认是可以用在任何元素上:

  • ElementType.ANNOTATION_TYPE 用在注解类型上
  • ElementType.CONSTRUCTOR 用在构造函数上
  • ElementType.FIELD 用在字段声明上(包括枚举常量)
  • ElementType.LOCAL_VARIABLE 用在局部变量上
  • ElementType.METHOD 用在方法声明上
  • ElementType.PACKAGE 用在包声明上
  • ElementType.PARAMETER 用在方法的参数上
  • ElementType.TYPE 用在类、接口(包括注解类型)或枚举生命上。

@Documented

指示某一类型的注释将通过 javadoc 和类似的工具进行文档化输出,默认情况下,注解是不会被Javadoc工具输出到文档上的。

@Inherited

表示注解类型可以从超类中继承,默认是不能从超类继承的,当用户查询注解类型,并且类没有此类型的注解时,将查询类的超类以获取注解类型,此注解仅适用于类声明。

@Repeatable

表示该注解能够多次标注到同一个地方,为了便于理解,举例说明:

如我给某些人授权一些事情,可能需要给多人。

    @Auth(role="Manager")    @Auth(role="Administrator")    public void doAuth() {...}@Repeatable(Auth.class)@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD,ElementType.PARAMETER})public @interface Auth {    String role() default "";}

使用了两个@Auth标志,这种用法在java 1.8之前是不被允许的,1.8后增加了@Repeatable注解。

@Native

表示定义常量值的字段可以被native代码引用,当native代码和java代码都需要维护相同的常量时,如果java代码使用了@Native标志常量字段,可以通过工具将它生成native代码的头文件。

注解的定义及使用

定义注解

定义注解和定义接口相识,只不过定义接口的关键字是interface,而定义注解的关键字是@interface,如下面就是一个完全合法的注解:

public @interface ClassPreamble {    String author();    String date();    int currentRevision() default 1;    String lastModified() default "N/A";    String lastModifiedBy() default "N/A";    // Note use of array    String[] reviewers();}

注解中可以包含默认值,如上面的default 1,表示如果用户没声明该字段,其默认值即为1。

然后就可以使用该注解了

@ClassPreamble(        author = "John Doe",        date = "1/7/2017",        currentRevision = 6,        lastModified = "12/31/2016",        lastModifiedBy = "Jhon",        reviewers = {"Alice", "Bob", "Cindy"})public class Test {}

注解的使用

代替文档注释

上面的例子中,我们可以将其改造成用作文档注释:

@Documentedpublic @interface ClassPreamble {    String author();    String date();    int currentRevision() default 1;    String lastModified() default "N/A";    String lastModifiedBy() default "N/A";    // Note use of array    String[] reviewers();}

运行时通过反射使用注解

上面的例子中,并没有显示的声明注解的生命周期和作用域,一般情况下不会这样做,特别是声明周期,如果要用在反射机制上,必须定义,因为默认的是CLASS类型,无法用在反射机制上,下面定义一个注解:

@Target({ElementType.TYPE,        ElementType.CONSTRUCTOR,        ElementType.FIELD,        ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface Member {    String name() default "";    int id() default -1;}

上面的元注解@Target中有多个值,@Target中的value是ElementType[]类型的,使用多个值是就需要使用{}括起来了。

然后建立一个类,使用该注解:

@Member(name = "test", id = 0)public class UserMember {    @Member(name = "zhanshan", id = 1)    private int age;    @Member(name = "lisi")    public void A() {    }}

然后可以通过反射找到注解:

public class ParseMember {    public static void parseMember() {        Annotation[] annotations = UserMember.class.getAnnotations();        for (Annotation annotation : annotations) {            Member member = (Member) annotation;            System.out.println("name: " + member.name() + " id: " + member.id());        }    }}

上述实例比较粗糙,如何使用反射,最好还是仔细研究下java的文档。

0 0
原创粉丝点击