Java内置系统注解和元注解

来源:互联网 发布:工业数据采集难点 编辑:程序博客网 时间:2024/04/29 21:37

第一节:注解(Annotation)的作用

       Annotation(注解)是JDK5.0及以后版本引入的。它的作用是修饰程序元素。什么是程序元素呢?例如:包、类、构造方法、方法、成员变量等。

      注解,就是对某一事物进行添加注释说明,会存放一些信息,这些信息可能对以后某个时段来说是很有用处的。
     java提供了一套机制,使得我们可以对方法、类、参数、包、域以及变量等添加标注(即附上某些信息)。且在以后某个时段通过反射将标注的信息提取出来以供使用。 
    注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记。程序可以利用java的反射机制来了解你的类及各种元素上有无何种标记,针对不同的标记,就去做相应的事件。

第二节:定义注解

      定义新的Annotation类型使用@interface关键字(在原有interface关键字前增加@符号)。定义一个新的Annotation类型与定义一个接口很像,例如:

public @interface Test{}

定义完该Annotation后,就可以在程序中使用该Annotation。使用Annotation,非常类似于public、final这样的修饰符,通常,会把Annotation另放一行,并且放在所有修饰符之前。例如:

@Testpublic class MyClass{....}

根据注解是否包含成员变量,可以把注解分为如下两类:

  • 标记注解:没有成员变量的Annotation被称为标记。这种Annotation仅用自身的存在与否来为我们提供信息,例如@override等。

  • 元数据注解:包含成员变量的Annotation。因为它们可以接受更多的元数据,因此被称为元数据Annotation。 成员以无参数的方法的形式被声明,其方法名和返回值定义了该成员变量的名字和类型。

 成员变量

Annotation只有成员变量,没有方法。Annotation的成员变量在Annotation定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。例如:

public @interface MyTag{    string name();    int age();}

示例中定义了2个成员变量,这2个成员变量以方法的形式来定义。

一旦在Annotation里定义了成员变量后,使用该Annotation时就应该为该Annotation的成员变量指定值。例如:

public class Test{    @MyTag(name="红薯",age=30)    public void info(){    ......    }}

也可以在定义Annotation的成员变量时,为其指定默认值,指定成员变量默认值使用default关键字。示例:

public @interface MyTag{    string name() default "我兰";    int age() default 18;}

如果Annotation的成员变量已经指定了默认值,使用该Annotation时可以不为这些成员变量指定值,而是直接使用默认值。例如:

public class Test{    @MyTag    public void info(){    ......    }}
如果注解只有一个成员变量,则建议取名为value,在使用时可用忽略成员名和赋值符=

 

第三节:基本注解

      

在java.lang包下,JAVA提供了5个基本注解::

  • @Override 

       限定重写父类方法。对于子类中被@Override 修饰的方法,如果存在对应的被重写的父类方法,则正确;如果不存在,则报错。@Override 只能作用于方法,不能作用于其他程序元素。

  • @Deprecated

       用于表示某个程序元素(类、方法等)已过时。如果使用了被@Deprecated修饰的类或方法等,编译器会发出警告。

  • @SuppressWarnings

        抑制编译器警告。指示被@SuppressWarnings修饰的程序元素(以及该程序元素中的所有子元素,例如类以及该类中的方法.....)取消显示指定的编译器警告。例如,常见的@SuppressWarnings(value="unchecked")

          SuppressWarnings注解的常见参数值的简单说明:

                1.deprecation:使用了不赞成使用的类或方法时的警告(使用@Deprecated使得编译器产生的警告);
    2.unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型; 关闭编译器警告
    3.fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告;
    4.path:在类路径、源文件路径等中有不存在的路径时的警告;
    5.serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告;
    6.finally:任何 finally 子句不能正常完成时的警告;
    7.all:关于以上所有情况的警告。

  • @SafeVarargs

       @SafeVarargs是JDK 7 专门为抑制“堆污染”警告提供的。

  • @FunctionalIterface   (java 8 新增的)

        函数式接口。Java8规定:如果接口中只有一个抽象方法(可以包含多个默认方法或多个static方法),该接口称为函数式接口。

        @FunctionalInterface就是用来指定某个接口必须是函数式接口,否则就会编译出错。

   @FunctionalInterface

public interface Fun

{

   static void foo()

{

    System.out.println("foo类方法");

}

default void bar()

{

    System.out.println("bar默认方法");

}

void test();//只定义了一个抽象方法

}

如在上面的接口中再加一个抽象方法abc(),则会编译出错。

 

第四节 元注解

在java.lang.annotation包下,定义了6个元注解。元注解就是修饰注解的注解。

       拿到一个注解,如何知道它是否是元注解呢?需要看它的元注解(无论是元注解还是普通注解都是有元注解的),如果看到这样的元注解:@Target(ElementType.ANNOTATION_TYPE),那么此时这个注解一定是元注解。

  • @Retention

  • @Target

  • @Documented

  • @Inherited

  • @Repeatable  (java 8新增)

  • 类型注解

@Repetable和类型注解在另外一篇博文中介绍。

 

4.1 @Retention

@Retention用于指定注解可以保留多长时间(生命周期)。

@Retention包含一个名为“value”的成员变量,该value成员变量是RetentionPolicy枚举类型。使用@Retention时,必须为其value指定值。value成员变量的值只能是如下3个:

  • RetentionPolicy.SOURCE:Annotation只保留在源代码中,编译器编译时,直接丢弃这种Annotation,不记录在.class文件中。

  • RetentionPolicy.CLASS:编译器把Annotation记录在class文件中。当运行Java程序时,JVM中不可获取该Annotation信息。这是默认值

  • RetentionPolicy.RUNTIME:编译器把Annotation记录在class文件中。当运行Java程序时,JVM可获取该Annotation信息,程序可以通过反射获取该Annotation的信息。

示例:

package com.demo1;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;//name=value形式//@Retention(value=RetentionPolicy.RUNTIME)//直接指定@Retention(RetentionPolicy.RUNTIME)public @interface MyTag{String name() default "我兰";}

如果Annotation里有一个名为“value“的成员变量,使用该Annotation时,可以直接使用XXX(val)形式为value成员变量赋值,无须使用name=val形式。

4.2 @Target

@Target指定Annotation用于修饰哪些程序元素。@Target也包含一个名为”value“的成员变量,该value成员变量类型为ElementType[ ]ElementType为枚举类型,值有如下几个:

  • ElementType.TYPE:能修饰类、接口或枚举类型

  • ElementType.FIELD:能修饰成员变量

  • ElementType.METHOD:能修饰方法

  • ElementType.PARAMETER:能修饰参数

  • ElementType.CONSTRUCTOR:能修饰构造器

  • ElementType.LOCAL_VARIABLE:能修饰局部变量

  • ElementType.ANNOTATION_TYPE:能修饰注解

  • ElementType.PACKAGE:能修饰包

示例1(单个ElementType):

package com.demo1;import java.lang.annotation.ElementType;import java.lang.annotation.Target;@Target(ElementType.FIELD)public @interface AnnTest {String name() default "sunchp";}

示例2(多个ElementType):

package com.demo1;import java.lang.annotation.ElementType;import java.lang.annotation.Target;@Target({ ElementType.FIELD, ElementType.METHOD })public @interface AnnTest {String name() default "sunchp";}

4.3 @Documented

如果定义注解A时,使用了@Documented修饰定义,则在用javadoc命令生成API文档后,所有使用注解A修饰的程序元素,将会包含注解A的说明。

示例:

@Documentedpublic @interface Testable {}
public class Test {@Testablepublic void info() {}}

4.4 @Inherited

@Inherited指定注解具有继承性。如果某个类使用了@xxx注解(定义该注解时使用了@Inherited修饰)修饰,则其子类将自动被@xxx修饰

示例:

package com.demo2;import java.lang.annotation.ElementType;import java.lang.annotation.Inherited;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Inheritedpublic @interface MyTag{}
package com.demo2;@MyTagpublic class Base {}
package com.demo2;//SubClass只是继承了Base类//并未直接使用@MyTag注解修饰public class SubClass extends Base {public static void main(String[] args) {System.out.println(SubClass.class.isAnnotationPresent(MyTag.class));}}

示例中Base使用@MyTag修饰,SubClass继承Base,而且没有直接使用@MyTag修饰,但是因为MyTag定义时,使用了@Inherited修饰,具有了继承性,所以运行结果为true。

如果MyTag注解没有被@Inherited修饰,则运行结果为:false。

 

 

第五节:注解的语法与定义形式

(1)以@interface关键字定义

(2)注解需要标明注解的生命周期,注解的修饰目标等信息,这些信息是通过元注解实现。
上面的语法不容易理解,下面通过例子来说明一下,这个例子就是Target注解的源码,

源码分析如下:
第一:元注解@Retention,成员value的值为RetentionPolicy.RUNTIME。
第二:元注解@Target,成员value是个数组,用{}形式赋值,值为ElementType.ANNOTATION_TYPE
第三:成员名称为value,类型为ElementType[]
另外,需要注意一下,如果成员名称是value,在赋值过程中可以简写。如果成员类型为数组,但是只赋值一个元素,则也可以简写。如上面的简写形式为:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)

第六节:注解的底层实现

定义一个注解:

分析其字节码,如下图所示:
C:\Users\Administrator\workspace\cache\bin>javap -verbose Cache

分析上面的字节码,我们可以得出:
第一:
public interface Cache extends java.lang.annotation.Annotation,说明Cache注解是继承自Annotation,仍然是interface。
第二:
public abstract java.lang.String value(),说明value方法是abstract类型。

 
 

当开发者使用了注解修饰了类、方法、成员变量等成员之后,这些注解不会自己生效,必须由开发者提供相应的代码来提取并处理注解信息。这些提取和处理注解的代码统称为APT(Annotation Processing Tool)注解处理工具。Annotion(注解)是一个接口,程序可以通过反射来获取指定程序元素的Annotion对象,然后通过Annotion对象来获取注解里面的元数据。这一部分在《Java自定义注解中讲解》 。

0 0