Java注解

Annotation(注解)

定义

  • 形象理解:想像代码具有生命,注解就是对于代码中某些鲜活个体的贴上去的一张标签。简化来讲,注解如同一张标签。
  • 官方释义:注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。注解有许多用处,主要如下:

    • 提供信息给编译器: 编译器可以利用注解来探测错误和警告信息
    • 编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
    • 运行时的处理: 某些注解可以在程序运行的时候接受代码的提取

特点

  • 注解不支持继承,因此不能使用关键字extends来继承某个@interface,但注解在编译后,编译器会自动继承java.lang.annotation.Annotation接口

分类

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

  • 标记注解
    没有定义成员变量的注解,仅仅利用自身的存在与否来提供信息
  • 元数据注解
    包含成员变量的注解,可以接受元数据。

注解的定义和使用

定义注解使用@interface关键字,可以包含属性。注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以无形参的方法形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。在使用的时候,我们应该给注解的属性进行赋值。如果指定了默认值,使用该注解时可以不为有默认值的成员变量指定值。
在注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组

//代码定义了 TestAnnotation 这个注解中拥有 id 和 msg 两个属性。==============普通应用
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {

    int id() default 0; //指定了默认值为0

    String msg();

}

//使用时需赋值,方式是在注解的括号内以 value="" 形式,多个属性之前用 ,隔开
@TestAnnotation(id=3,msg="hello annotation")
public class Test {

}

//注解中属性可以有默认值,默认值需要用 default 关键值指定。==============设置默认值
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {

    public int id() default -1;

    public String msg() default "Hi";

}
//TestAnnotation 中 id 属性默认值为 -1,msg 属性默认值为 Hi。 它可以这样应用:
@TestAnnotation()
public class Test {}

//如果一个注解内仅仅只有一个名字为 value 的属性时,应用这个注解时可以直接接属性值填写到括号内。==============仅一个属性
public @interface Check {
    String value();
}

@Check("hi")
int a;

//这和下面的效果是一样的
@Check(value="hi")
int a;

//最后,还需要注意的一种情况是一个注解没有任何属性。比如==============没有属性
public @interface Perform {}

//那么在应用这个注解的时候,括号都可以省略。
@Perform
public void testMethod(){}

注解的提取

通过用标签来比作注解,前面的内容是讲怎么写注解,然后贴到哪个地方去,而现在我们要做的工作就是检阅这些标签内容。 形象的比喻就是你把这些注解标签在合适的时候撕下来,然后检阅上面的内容信息。

注解的提取需要借助于 Java 的反射技术。反射比较慢,所以注解使用时也需要谨慎计较时间成本

//举例:检阅注解在类上的注解
@TestAnnotation()
public class Test {

    public static void main(String[] args) {

        //通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解
        boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);

        if ( hasAnnotation ) {

            //通过 getAnnotation() 方法来获取 Annotation 对象
            TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
            //或者是 getAnnotations() 方法。
            //public Annotation[] getAnnotations() {}
            //前一种方法返回指定类型的注解,后一种方法返回注解到这个元素上的所有注解。

            //如果获取到的 Annotation 如果不为 null,则就可以调用它们的属性方法了
            System.out.println("id:"+testAnnotation.id());
            System.out.println("msg:"+testAnnotation.msg());
        }
    }
}

//举例:检阅属性、方法上的注解
@TestAnnotation(msg="hello")
public class Test {

    @Check(value="hi")
    int a;


    @Perform
    public void testMethod(){}


    @SuppressWarnings("deprecation")
    public void test1(){
        Hero hero = new Hero();
        hero.say();
        hero.speak();
    }


    public static void main(String[] args) {

        boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);

        if ( hasAnnotation ) {
            TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
            //获取类的注解
            System.out.println("id:"+testAnnotation.id());
            System.out.println("msg:"+testAnnotation.msg());
        }


        try {
            Field a = Test.class.getDeclaredField("a");
            a.setAccessible(true);
            //获取一个成员变量上的注解
            Check check = a.getAnnotation(Check.class);

            if ( check != null ) {
                System.out.println("check value:"+check.value());
            }

            Method testMethod = Test.class.getDeclaredMethod("testMethod");

            if ( testMethod != null ) {
                // 获取方法中的注解
                Annotation[] ans = testMethod.getAnnotations();
                for( int i = 0;i < ans.length;i++) {
                    System.out.println("method testMethod annotation:"+ans[i].annotationType().getSimpleName());
                }
            }
        } catch (NoSuchFieldException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println(e.getMessage());
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println(e.getMessage());
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println(e.getMessage());
        }
    }
}

基本注解

@Override

提示子类要复写父类中的方法时,用 @Override 修饰要复写的方法

@Deprecated

用来标记过时的元素,想必大家在日常开发中经常碰到。编译器在编译阶段遇到这个注解时会发出提醒警告,告诉开发者正在调用一个过时的元素比如过时的方法、过时的类、过时的成员变量。比如:
定义了一个 Hero 类,它有两个方法 say() 和 speak() ,其中 say() 被 @Deprecated 注解。

public class Hero {

    @Deprecated
    public void say(){
        System.out.println("Noting has to say!");
    }


    public void speak(){
        System.out.println("I have a dream!");
    }
}

然后我们在 IDE 中分别调用它们。可以看到,say() 方法上面被一条直线划了一条,这其实就是编译器识别后的提醒效果。
这里写图片描述

@SuppressWarnings

阻止警告

@SafeVarags

参数安全类型注解。它的目的是提醒开发者不要用参数做一些不安全的操作,它的存在会阻止编译器产生 unchecked 这样的警告。

@FuntionnalInterface

函数式接口注解

元注解

存在于java.lang.annotation包下,共5个。用于修饰其他注解。

@Retention: 保留时间

Retention 的英文意为保留期的意思。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间。
它的取值如下:

  • RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
  • RetentionPolicy.CLASS 注解只被保留到编译阶段,它并不会被加载到 JVM 中。
  • RetentionPolicy.RUNTIME 注解可以保留到程序运行阶段,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
//应用举例
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
}

@Target: 运用元素

@Target 指定了注解运用的地方。
原本标签是你想张贴到哪个地方就到哪个地方,但是因为 @Target 的存在,它张贴的地方就非常具体了,比如只能张贴到方法上、类上、方法参数上等等。
@Target 有下面的取值:

  • ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
  • ElementType.CONSTRUCTOR 可以给构造方法进行注解
  • ElementType.FIELD 可以给属性进行注解
  • ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
  • ElementType.METHOD 可以给方法进行注解
  • ElementType.PACKAGE 可以给一个进行注解
  • ElementType.PARAMETER 可以给一个方法内的参数进行注解
  • ElementType.TYPE 可以给一个型进行注解,比如类、接口、枚举

@Documented

@Documented能够将注解中的元素包含到 Javadoc 中去

@Inherited

如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。

//应用举例
//注解 Test 被 @Inherited 修饰,之后类 A 被 Test 注解,类 B 继承 A,类 B 也拥有 Test 这个注解。
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}


@Test
public class A {}


public class B extends A {}

@Repeatable

Java 1.8 才加进来的,表示该注解可重复使用

//@Repeatable 注解了 Person。而 @Repeatable 后面括号中的Persons类相当于一个容器注解。
//什么是容器注解呢?就是用来存放其它注解的地方。它本身也是一个注解。按照规定,它里面必须要有一个 value 的属性,属性类型是一个被 @Repeatable 注解过的注解数组,注意它是数组

@interface Persons {
    Person[]  value();
}


@Repeatable(Persons.class)
@interface Person{
    String role default "";
}


@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan{

}

Spring相关框架常用注解

@Controller

@Controller 用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller 对象。
分发处理器将会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping 注解。@Controller 只是定义了一个控制器类,而使用@RequestMapping 注解的方法才是真正处理请求的处理器。单单使用@Controller 标记在一个类上还不能真正意义上的说它就是SpringMVC 的一个控制器类,因为这个时候Spring 还不认识它。那么要如何做Spring 才能认识它呢?这个时候就需要我们把这个控制器类交给Spring 来管理。有两种方式:

  (1)在SpringMVC 的配置文件中定义MyController 的bean 对象。

  (2)在SpringMVC 的配置文件中告诉Spring 该到哪里去找标记为@Controller 的Controller 控制器。

@RestController

继承自@Controller注解

@RequestMapping

可用于类或方法上,用来处理请求地址映射的注解。

用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
用方法上,value 表示访问该方法的 URL 地址。
RequestMapping注解有六个属性:

  • value: 指定请求的实际地址,指定的地址可以是URI Template 模式(后面将会说明);
  • method: 指定请求的method类型, GET、POST、PUT、DELETE等;
  • consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
  • produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
  • params: 指定request中必须包含某些参数值是,才让该方法处理。
  • headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求。

@Configuration

@Configuration 是一个类级注释,指示对象是一个bean定义的源。@Configuration 类通过 @bean 注解的公共方法声明bean。


参考文献

https://blog.csdn.net/briblue/article/details/73824058 —注解入门
https://blog.csdn.net/javazejian/article/details/71860633 —深入理解Java注解类型
https://www.cnblogs.com/leskang/p/5445698.html —springmvc常用注解标签详解
https://www.cnblogs.com/caoyc/p/5635173.html —@RequestMapping

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值