09_Java注解

本章章节

> 9.1 注解(Annotation)

> 9.2 JDK提供的几个基本注解

> 9.3 自定义注解

 

9.1 注解(Annotation)简介

  Annotation(注解) JDK1.5之后增加的一个新特性,这种特性被称为元数据特性,在JDK1.5之后称为注解。注解使得Java源代码中不但可以包含功能性的实现代码,还可以添加元数据。注解的功能类似于代码中的注释,它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明、注释。所不同的是注解不是提供代码功能的说明,而是实现程序功能的重要组成部分。Java注解已经在很多框架中得到了广泛的使用,用来简化程序中的配置。代码分析工具、开发工具和部署工具可以通过注解来进行验证或进行部署。它的作用非常的多,例如:进行编译检查、生成说明文档、代码分析等。

9.2 JDK提供的几个基本注解

  注解的语法比较简单,除了@符号的使用之外,它基本与Java固有的语法一致。java.lang.annotation.Annotation接口是所有的Annotation都必须实现的接口。在JDK1.5 之后,系统中已经建立了如下的三个内建标准Annotation类型,用户直接使用即可。

  系统内建的三种标准Annotation

    - @Override:覆写的Annotation

    - @Deprecated:不赞成使用的Annotation(过期、失效)

    - @SuppressWarnings:压制安全警告的Annotation(忽略警告)

三种内定的标准Annotation如图9-1所示:

 

9-1  三种内定的Annotation

  在Idea中输入@Override,然后按F3可以定位到@OverrideJava中的声明位置。

  另外,Java还提供了四种元注解,它们专门负责新注解的创建。

  四种元注解:

   @Target:表示该注解用于什么地方。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
     ElementType[] value();
}

 

  可能的 ElemenetType 参数包括:

    ElemenetType.CONSTRUCTOR 构造器之前声明。

    ElemenetType.FIELD 域之前声明(包括 enum 实例)。

    ElemenetType.LOCAL_VARIABLE 局部变量之前声明。

    ElemenetType.METHOD 方法之前声明。

    ElemenetType.PACKAGE 包之前声明。

    ElemenetType.PARAMETER 方法参数之前声明。

    ElementType.ANNOTATION_TYPE 注解类型之前声明。

    ElemenetType.TYPE 类,接口(包括注解类型)或enum之前声明。

  @Retention:表示在什么级别保存该注解信息。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
     RetentionPolicy value();
}

 

  可选的RetentionPolicy参数包括:

    RetentionPolicy.SOURCE 表示此注解信息只能保留在程序的源文件中,编译之后不会保存在编译好的类文件(*.class)之中。

    RetentionPolicy.CLASS 表示此注解信息保留在程序源文件(*.java)和编译之后的类文件(*.class)之中,在使用此类的时候,这些注解信息不会被加载到JVM之中,如果一个注解声明时没有制定范围,则默认是此范围。

    RetentionPolicy.RUNTIME 表示此注解信息保留在源文件(*.java)、类文件(*.class)和执行时也会加载。即JVM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。

  举一个例子,如@Override里面的Retention设为SOURCE,编译成功了就不要这一些检查的信息,相反,@Deprecated里面的Retention设为RUNTIME,表示除了在编译时会警告我们使用了哪个被 Deprecated的方法,在执行的时候也可以查出该方法是否被Deprecated

  @ Documented:将此注解包含在javadoc中。

  @ Inherited:允许子类继承父类中的注解。

 

  9.2.1 @Override

  该注解用在方法前面,用来标识该方法是重写父类的某个方法。@Override表示方法覆写的正确性。用来检测子类覆盖父类方法,是否与父类里方法签名一致。如果你不小心拼写错误,或者方法原型与要覆盖的方法不一致,则编译器就会发出错误提示。

idea中输入@Override,然后按F3可以定位到@OverrideJava中的声明位置。如下就是@Override注解的声明:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

 

  除了@符号以外,@Override的定义很像一个空的接口。@Target(ElementType.METHOD)表示@Override注解可以用于什么地方,这里只能用于METHOD(方法)前面;@Rectetion(RectetionPolicy.SOURCE)用来定义注解在哪一个级别可用,SOURCE表示源代码中,即@Override会被编译器丢弃。

@Override使用举例:

class A{
  public void disp(){
  }
  }

class B extends A{
  @Override
  public void disp(int a){
  }
}

 

  如果不写@Override,则编译没有任何问题,但是加上@Override之后,表示现在的disp是覆盖父类disp方法,但是却跟父类的方法原型不一致,所以此时会报错。

 

  9.2.2 @Deprecated

  该注解的作用是标记某个过时的类或方法。使用@Deprecated注释的Annotation本身是不建议使用的一个操作,表示已过时。当使用时,可能会出现了一个安全的警告信息。

  在idea中输入@Deprecated,然后按F3可以定位到@DeprecatedJava中的声明位置。如下就是@Deprecated注解的声明:

@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Deprecated {
}

 

  @Documented表示@Deprecated注解包含在javadoc文档中。@Retention(RetentionPolicy.RUNTIME)表示在运行期间也保留@Deprecated注解。

  我们在idea中输入Date,然后按F3去查看java.util.Date这个类的说明时,可以发现,在该类中,有很多类似下面的语法:

  @Deprecated

      public Date(int year, int month, int date) {

          this(year, month, date, 0, 0, 0);

  }

  这些语法的特点是,在方法的前面加上了@Deprecated修饰之后,该方法会多一条横杠,表示此方法已经过期。如果我们还使用这个方法的话,编译器可能会提示警告信息。

 

  @Deprecated使用举例:

  @Deprecated //修饰类

class C{

  @Deprecated //修饰属性

  private String name = "zhangsan";

  @Deprecated //修饰方法

  public int add(int a, int b){

    return a + b;

  }

}

 

  9.2.3 @SuppressWarnings

  @SuppressWarnings 用于压制警告信息。通常写完一个程序之后可能会有这样那样的警告信息,可以利用@SuppressWarnings来消除。

  在idea中输入@Deprecated,然后按F3可以定位到@DeprecatedJava中的声明位置。如下就是@Deprecated注解的声明:

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
   String[] value();
}

 

  @Target({TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})表示@SuppressWarnings可以用于类、接口、枚举、域声明、方法、参数、构造器、局部变量的前面。@Retention(RetentionPolicy.SOURCE)表示  @SuppressWarnings只能在源文件中有效。内部是一个String[] value();数组属性,表示可以列举多项。

  @SuppressWarnings使用举例:

public class Test {
  public static void main(String[] args) {
  @SuppressWarnings("all")
  int i = 0;
  }
}

 

  定义变量i没有使用过,会提示警告,通过@SuppressWarning ("all") 取消所有警告。可以同时压制多个警告信息,只需要以数组的形式出现即可。  

    如:@SuppressWarnings({"unused", "serial", "deprecation"})

关于@SuppressWarnings()中列举的含义参考下表:

 

9.3 自定义注解

  9.3.1 定义注解

  定义简单的注解的格式:

    //@Target(适用场合)

    //@Retention(保存级别)

    //@Documented

    //@Inherited

    [public] @interface 注解名称{

      数据类型  变量名称();

    }

  

  如果定义注解时没有指定@Target元注解来限制它的使用范围,那么该注解可以使用在 ElementType枚举指定的任何一个元素前。否则,只能声明在@Target元注解指定的元素前。

  如果定义注解时没有指定@Retention元注解来说明它的生命周期,那么该注解默认的生命周期是保留在一个CLASS文件中。当然,也可以由一个@Retetion的元注解指定它的生命周期。

  所有的注解默认情况下都是使用@Documented进行注释,而且在生成javadoc的时候可以通过@Documented设置一些说明信息。

下面是一个定义注解的实例:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAnnotation {
String value();
}

 

  其中的@interface是一个关键字,用来声明一个注解。在设计annotation的时候必须把一个类型定义为@interface,而不能用classinterface关键字。所有的注解类都隐式继承于java.lang.annotation.Annotation,注解不允许显式继承于其他的接口。

  一个注解可以拥有多个成员,成员声明和接口方法声明类似,这里,我们仅定义了一个成员,成员的声明有以下几点限制:

    ·成员以无参无抛出异常的方式声明,如“boolean value(String str);”“boolean value() throws Exception等方式是非法的;

    ·可以通过default为成员指定一个默认值,如:“String level() default "LOW_LEVEL";”“int[] high() default {2, 3, 4};”是合法的,当然也可以不指定默认值;

    ·成员类型是受限的,合法的类型包括原始类型及其包装类、StringClassenums、注解类型,以及上述类型的数组类型。如“ForumService value();”“List foo();”是非法的。

    ·如果注解只有一个成员,则成员名通常取名为value(),这样在使用时可以忽略成员名和赋值号(=),如@ MyAnnotation ("使用注解的实例")。注解类拥有多个成员时,如果仅对value成员进行赋值则也可不使用赋值号,如果同时对多个成员进行赋值,则必须使用赋值号,如@DeclareParents (name = "张三", age = 25)

    ·注解类可以没有成员,没有成员的注解称为标识注解,解释程序以标识注解存在与否进行相应的处理;

    ·在操作中,对于一个注解而言有时候会固定其取值范围,只能取固定的几个值,那么这个时候实际上就需要依靠枚举。

 

  9.3.2 使用注解

  使用注解的语法:

  一般格式:

    @注解名(成员名1=成员值1, 成员名2=成员值2, ...)

  如果是成员的类型是数组:

    @注解名(成员名1={成员值1, 成员值2…}, 成员名2=成员值2, ...)

  下面是一个使用注解注解的实例。

@MyAnnotation(value="my Annnotation")
public class TestAnnotation {
  //
}

 

  如果成员是数组类型,可以通过{}进行赋值,如boolean数组的成员可以设置为{true, false, true}。下面是几个注解使用的例子:

    ·多成员的注解

      @MyAnnotation(id= 2868724, synopsis = "Enable time-travel", engineer = "Mr. Peabody")

    ·一个成员的注解,且成员名为value,可以省略成员名和赋值符号

      @ MyAnnotation ("my Annnotation")

    ·无成员的注解

      @Override

    ·成员为字符串数组的注解

      @SuppressWarnings(value={"unchecked", "fallthrough"})

    ·成员为注解数组类型的注解

//MyAnno.java
import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAnno {
  String[] value();
}

 

//MyAnnotation.java
import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited

public @interface MyAnnotation {
  String[] name();
  int age() default 25;
  MyAnno[] anno();
}

 

//TestAnnotation.java
@MyAnnotation(name={"aaa", "bbb"}, anno={@MyAnno(value={"aaa", "bbb"}), @MyAnno(value={"ccc", "ddd", "eee"})})
public class TestAnnotation {
  public static void main(String[] args) {
  }
}

 

  9.3.3 得到注解

  一个注解要想让其变得有意义,则必须结合发射机制取得注解中设置的全部内容。对于生命周期为运行期间的注解,都可以通过反射获得该元素上的注解实例。

    1、声明在一个类中的注解

      可以通过该类 Class 对象的 getAnnotation getAnnotations 方法获得。

    2、声明在一个字段中的注解

      通过 Field 对象的 getAnnotation getAnnotations 方法获得

    3、声明在一个方法中的注解

      通过 Method 对象的 getAnnotation getAnnotations 方法获得

 

感谢阅读。如果感觉此章对您有帮助,却又不想白瞟

                                 

 

 

 

posted @ 2020-08-23 17:06  SpringL  阅读(153)  评论(0编辑  收藏  举报