专栏首页代码拾遗​Java 注解详解

​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

本文分享自微信公众号 - 代码拾遗(gh_8f61e8bcb1b1)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-05-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 反射基础之Constructor

    构造器的声明包含了:名字,修饰符,参数和异常。可以通过java.lang.reflect.Constructor类获取这些信息。 下面的例子描述了如何获取构造器...

    代码拾遗
  • 反射基础之Method

    一个方法声明包括:方法名,描述符,参数,返回类型和异常。可以通过java.lang.reflect.Method类获取这些信息。 下面的例子说明了如何获取一个类...

    代码拾遗
  • Java8 Date Time API

    Java8 带来了全新的处理日期和时间的方式。几乎所有人都有使用Java Date API痛苦的经历。因此有很多人切换到了Joda Time,但是Java8现在...

    代码拾遗
  • Android | dagger细枝篇

    嗨,我是哈利迪~《看完不忘系列》之dagger(树干篇)一文对dagger做了初步介绍,下面我们一起来瞅瞅dagger的一些细节。

    Holiday
  • SpringBoot自动装配原理解析

    我们知道,在使用SpringBoot的时候,我们只需要如下方式即可直接启动一个Web程序:

    Java学习录
  • Java 中的注解有哪些?如何自定义注解?(高级岗位必背)

    熟悉 C#的人应该知道,C#中的 Attribute 类,实现对元数据(数据的数据)的编程支持。

    水货程序员
  • 面试官,怎样实现 Router 框架?

    Android 开发中,组件化,模块化是一个老生常谈的问题。随着项目复杂性的增长,模块化是一个必然的趋势。除非你能忍受改一下代码,就需要六七分钟的漫长时间。

    用户2965908
  • SpringBoot 应用篇之从 0 到 1 实现一个自定义 Bean 注册器

    我们知道在 spring 中可以通过@Component,@Service, @Repository 装饰一个类,通过自动扫描注册为 bean;也可以通过在配置...

    一灰灰blog
  • 掌握@ControllerAdvice配合RequestBodyAdvice/ResponseBodyAdvice使用,让你的选择不仅仅只有拦截器【享学Spring MVC】

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    YourBatman
  • 深入Proxy底层源码——实现自己的JDK动态代理

    2.使用jdk动态代理获取代理类对象(JDK自动生成代理类) $Proxy0.class,使用反编译工具

    须臾之余

扫码关注云+社区

领取腾讯云代金券