​Java 注解详解

时间:2022-06-04
本文章向大家介绍​Java 注解详解,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
简介

注解是在Java 1.5之后引入的,为的是可以直接在代码中编写元信息。在注解发布之前,如果想要描述代码只能将其写入到其他文件中,例如xml。 注解可以应用于包,类,方法,字段,参数,类型(Java 8之后)。注解并不会直接影响代码,它只是为第三方系统提供代码的元信息,第三方系统通过解析这些注解获取信息,从而执行不同的方法。

注解的语法

注解使用@开头,例如

@Annotation
public void annotatedMethod(){
}

注解同样也可以有参数

@Annotation(
   info = "I am an annotation",
   counter = "55"
)
public void annotatedMehod() {
...
 }

如果只有一个参数,参数名可以省略

@Annotation("I am an annotation")
public void annotatedMehod() {
...
 }

多个注解可以同时修饰同一个元素

@Annotation (info = "UauO")
@Annotation2
class AnnotatedClass { ... }
用途

注解有多种用途,最常用的几种方式是:

  • 提供信息给编译器: 编译器可以分析注解,获取不同的规则产生警告甚至错误。例如Java 8的 @FunctionInterface 注解,如果这个注解修饰的接口包含了2个及以上的方法编译器就会发出错误警告
  • 生成文档: 一些特殊的注解处理其可以通过解析特定注解生成文档,例如Jenkins等
  • 代码生成: 通过注解提供的信息,自动生成代码,例如JAXB
  • 运行时处理: 在运行时分析注解,例如Spring,JPA等
内置注解

Java内置了一些常用的注解,例如下面要介绍的元注解,之所以叫元注解是因为它们是修饰注解的注解

  • @Retention 表示注解的声明周期,可选值为:
    • SOURCE: 仅存在于源码中,被编译器和JVM忽略
    • CLASS: 保存在字节码中,编译器可以获取,JVM忽略
    • RUNTIME: 运行时可以获取,整个生命周期都可以获取
  • @Target 表示注解可以修饰那些元素
    • ANNOTATION_TYPE: 可以修饰注解
    • CONSTRUCTOR: 可以修饰构造函数
    • FIELD: 可以修饰字段
    • LOCAL_VARIABLE: 可以修饰本地变量
    • METHOD: 可以修饰方法
    • PACKAGE: 可以修饰包
    • PARAMETER: 可以修饰方法参数
    • TYPE: 可以修饰类
  • @Documented: 可以由Javadoc 工具生成文档
  • @Inherited: 默认注解是不能被子类继承的,这个注解修饰后注解可以被所有子类继承。
  • @Deprecated: 表示被修饰的元素已经被遗弃了,以后不再维护。
  • @SuppressWarning: 通知编译器不要为被修饰的元素产生错误
  • @Override: 子类重写父类方法。
  • @SafeVarargs: 方法或者构造函数的可变参数不会执行不安全的操作,具体可参考@SafeVarargs
  • @Repeatable: Java 8提供注解,表示注解可以重复 例如,没有这个注解的之后,同一个注解在同一个元素上使用一次,所以如果包含了多个值需要使用数组的形式 @Retention( RetentionPolicy.RUNTIME ) @Target( ElementType.TYPE_USE ) public @interface RepeatedValues { CanBeRepeated[] value(); } 有了@Repeatable之后可以直接定义这个注解是可以重复使用的 @Retention( RetentionPolicy.RUNTIME ) @Target( ElementType.TYPE_USE ) @Repeatable( RepeatedValues.class ) public @interface CanBeRepeated { String value(); } @CanBeRepeated( "the color is green" ) @CanBeRepeated( "the color is red" ) @CanBeRepeated( "the color is blue" ) public class RepeatableAnnotated { }
  • @FunctionInterface: 修饰接口表示其是一个函数式接口,只能包含一个函数声明。
自定义注解

自定义注解的关键字是@interface 例如:

@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.TYPE )
public @interface CustomAnnotationClass
{

    public String author() default "danibuiza";

    public String date();

}

上述定义了一个注解CunstomAnnotationClass,它由两个参数author和date,author的默认值是 ‘danibuiza’,这个注解可以修饰类,且在运行时可见。 使用注解

@CustomAnnotationClass( date = "2014-05-05" )
public class AnnotatedClass
{ 
...
}

java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Field等都实现了getAnnotations(),isANnotationPresent(Annotation),getAnnotation(class)这些方法,这些方法是使用自定义注解的主要方法,例如:

public static void main( String[] args ) throws Exception
{

    Class<AnnotatedClass> object = AnnotatedClass.class;
    // Retrieve all annotations from the class
    Annotation[] annotations = object.getAnnotations();
    for( Annotation annotation : annotations )
    {
        System.out.println( annotation );
    }

    // Checks if an annotation is present
    if( object.isAnnotationPresent( CustomAnnotationClass.class ) )
    {

        // Gets the desired annotation
        Annotation annotation = object.getAnnotation( CustomAnnotationClass.class );

        System.out.println( annotation );

    }
    // the same for all methods of the class
    for( Method method : object.getDeclaredMethods() )
    {

        if( method.isAnnotationPresent( CustomAnnotationMethod.class ) )
        {

            Annotation annotation = method.getAnnotation( CustomAnnotationMethod.class );

            System.out.println( annotation );

        }

    }
}

运行后输出如下:

@com.danibuiza.javacodegeeks.customannotations.CustomAnnotationClass(getInfo=Info, author=danibuiza, date=2014-05-05)

@com.danibuiza.javacodegeeks.customannotations.CustomAnnotationClass(getInfo=Info, author=danibuiza, date=2014-05-05)

@com.danibuiza.javacodegeeks.customannotations.CustomAnnotationMethod(author=friend of mine, date=2014-06-05, description=annotated method)
@com.danibuiza.javacodegeeks.customannotations.CustomAnnotationMethod(author=danibuiza, date=2014-06-05, description=annotated method)
关于注解的继承问题

如果使用@Inherited修饰注解,那么子类是可以继承父类的注解的,但是这仅仅对于继承类的时候才有用,如果是实现接口,那么继承不再有效,例如:

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface InheritedAnnotation
{
}

@InheritedAnnotation 
public class AnnotatedSuperClass
{
    public void oneMethod()
    {
    }
}

public class AnnotatedSubClass extends AnnotatedSuperClass
{
    @Override
    public void oneMethod(){
    }
}

System.out.println( "is true: " + AnnotatedSuperClass.class.isAnnotationPresent( InheritedAnnotation.class ) );

System.out.println( "is true: " + AnnotatedSubClass.class.isAnnotationPresent( InheritedAnnotation.class ) );

输出:

is true: true
is true: true

而对于实现接口:

@InheritedAnnotation
public interface AnnotatedInterface
{
    public void oneMethod();
}

public class AnnotatedImplementedClass implements AnnotatedInterface
{
    @Override
    public void oneMethod()
    {
    }
}

System.out.println( "is true: " + AnnotatedInterface.class.isAnnotationPresent( InheritedAnnotation.class ) );

System.out.println( "is true: " + AnnotatedImplementedClass.class.isAnnotationPresent( InheritedAnnotation.class ) );

输出如下:

is true: true
is true: false