Java注解简单介绍

注解

1 注解的概述

1.1 注解是什么

​ 注解是JDK5的新特性,注解本身没有什么功能,但是javac编译器、开发工具或其他程序可以通过反射来查看你的程序是否有注解,也就是标记。根据不同的标记会做对应的事。注解可以用在包、类、构造器、成员变量、局部变量、方法、参数上。

1.2 常见的注解

  • @Override:标明此注解的方法IDEA/Eclipse等开发工具会帮助我们检查重写方法的语法
  • @Deprecated:标明此注解的方法、变量等代码在使用的时候IDEA/Eclipse等开发工具会将使用的方法或变量等划掉。
  • @SuppressWarnings:标明此注解的代码IDEA/Eclipse等开发工具抑制编译时的警告,就等于告诉编译器这个代码是安全的,不必警告。但是需要注意的是,有可能会把真的错误给隐藏了,用的时候尽量不要大范围使用。
  • @Author:用于生成文档的作者信息的。
  • @Version:用于生成文档的版本信息的。
  • @WebServlet:用于配置Servlet的,代替web.xml配置文件。
  • @FunctionalInterface:用于表明此接口是函数式接口,即有且只有一个抽象方法的接口,允许有默认方法(JDK8)、静态方法(JDK8)和私有方法(JDK9)。

2 自定义注解

​ 好了,基本了解的注解的一些概念和常见的注解,我们来自定义我们自己的注解。

2.1 定义注解的格式

定义格式

@元注解
public @interfcae 注解名{
	属性类型 属性名() default;
	...
}

注:中括号里面的default值可写可不写。

2.2 注解的数据类型

注解的数据类型必须是以下类型:

  • 基本数据类型
  • String类型
  • 枚举类型
  • 注解类型
  • Class类型
  • 以上数据类型的一维数组类型。

2.3 元注解

​ 上面定义的是简单的注解,不难发现上面定义的注解在类、成员变量、成员方法等地方都可以使用,如果我们想要让注解在那个位置使用,那就需要使用元注解来约束了。

常见的元注解:

  • @Target
  • @Retention
2.3.1 元注解:@Target

@Target注解是用来限定注解的使用位置,它的取值如下表所示

注解取值功能描述
ElementType.TYPE类,接口或者枚举,包括注解声明
ElementType.FIELD字段声明
ElementType.METHOD方法声明
ElementType.PARAMETER参数声明
ElementType.CONSTRUCTOR构造器声明
ElementType.LOCAL_VARIABLE局部变量声明
ElementType.ANNOTATION_TYPE注解类型声明
ElementType.PACKAGE包声明
ElementType.TYPE_PARAMETER类型变量的声明语句前,@since 1.8
ElementType.TYPE_USE应用于所有使用类型的任何语句(如:泛型,类型转换等),@since 1.8
ElementType.MODULEModuel声明,@since 9

注意:

  • ElementType.ANNOTATION_TYPE表示的是用此注解的注解A用于对注解B进行声明。例如@Target注解就是注解A,查看其源码即可看出来。

    @Target源码

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}
  • 鉴于有的老哥说ElementType.TYPE里没有注解声明,我贴一下JDK9源码,大家自行甄别,如果有错误请指出,^_^
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,
  • JDK8新增的两个值,类型注解

    ElementType.TYPE_USE,从源码中字面意思可以看成使用类型的地方,例如(泛型,类型转换、有返回值的方法,implements和throws语句)。

    ElementType.TYPE_PARAMETER,表示此注解可以在Type的声明语句前,可用在定义类或方法的泛型前面。

//在有返回值的方法
@MyAnnotation
public double run(){
    return 1.0;
}

//在泛型上
List<@MyAnnotation String> list = new ArrayList<>();

//在throws语句
public void run()throws @MyAnnotation Exception{
    System.out.println("--");
}

//类型转换
int  i = (@MyAnnotation int)10.6;

//implements后
public class task implements @MyAnnotation Runnable

//new对象时
Thread t = new @MyAnnotation Thread();
2.3.2 元注解:@Retention

​ 该注解表示自定义的注解保留到什么阶段,也就是生命周期。它的取值有三种,如下表所示。

注解取值功能描述
RetentionPolicy.SOURCE注解保留到源码阶段
RetentionPolicy.CLASS注解保留到字节码阶段
RetentionPolicy.RUNTIME注解保留到运行阶段,程序可以通过反射获取该注解

3 使用注解

2.1 自定义简单注解

了解到这里我们先不管元注解来创建一个简单的注解,元注解后面讲解。

自定义注解代码:

public @interface MyAnnotation {
    //int类型的属性,没有默认值
    int id() ;
    //double类型的属性,有默认值
    double salary() default 10000.0;
    //String数组类型的属性,没有默认值
    String[] info();
}

使用注解代码:

package com.gsgb.MyAnnotation;

@MyAnnotation(id=1,salary = 15000,info={"guo","li"})
public class AnnotationTest {
    
    private String name;

    @MyAnnotation(id=1,info={"guo","li"})
    public AnnotationTest(){ }

    @MyAnnotation(id=1,info={"guo","li"})
    public void run(String nickname){
        int num = 8 ;
        System.out.println(nickname+"run...");
    }

    public static void main(String[] args) {
        AnnotationTest annotation = new AnnotationTest();
        annotation.run("老王");
    }
}

通过上面的代码,我们不难发现,带有默认值的属性salary可写可不写,因此:

  • 有默认值的属性在使用注解的时候可以不赋值。
  • 没有默认值的属性在使用注解的时候必须赋值。

注:此注解因为没有约束,所以在包、类、字段、构造器、方法、参数、成员变量等位置都可以使用

2.2 特殊的属性名value

​ 注解有一个特殊的属性名叫value,该属性在使用注解的时候在有些情况下可以不写value名称,我们分情况讲解。

  • 当自定义注解只有value属性名时,在使用注解的时候可以省略value名称只写值。例如:

    属性:
    	String value();
    
    	@MyAnnotation("hello")
        public void run(String nickname){
            int num = 8 ;
            System.out.println(nickname+"run...");
        }
    
  • 当自定义注解除了value还有别的属性的时候,其他属性至少有一个没有默认值的时候,value不可省略。

    属性:
    	String value();
    	int id();
    
    	@MyAnnotation(value="hello",id=100)
        public void run(String nickname){
            int num = 8 ;
            System.out.println(nickname+"run...");
        }
    
  • 当自定义注解除了value还有别的属性的时候,其他属性有默认值的时候,value可省略

    属性:
    	String value();
        int id() default 50;
    
    	@MyAnnotation("hello")
        public void run(String nickname){
            int num = 8 ;
            System.out.println(nickname+"run...");
        }
    

2.3 元注解实例

自定义注解代码

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.CLASS)//表示注解保留到字节码文件
@Target(ElementType.METHOD)//表示注解是用于方法上的
public @interface MyAnnotation {
    //int类型的属性,没有默认值
    int id() ;
    //double类型的属性,有默认值
    double salary() default 10000.0;
    //String数组类型的属性,没有默认值
    String[] info();
}

自定义注解测试类

public class AnnotationTest {

    private String name;
    
    public AnnotationTest(){ }
	
    //该注解只能用在方法上,用在其他地方会报错
    //而且由于设置了RetentionPolicy.CLASS,所以此注解会保留到字节码文件
    @MyAnnotation(id=1,info={"guo","li"})
    public void run(String nickname){
        int num = 8 ;
        System.out.println(nickname+"run...");
    }

    public static void main(String[] args) {
        AnnotationTest annotation = new AnnotationTest();
        annotation.run("老王");
    }
}

4 注解解析

4.1 注解解析概念和相关方法

​ 注解解析就是通过代码获取注解数据的过程。

​ 与注解解析相关的接口有AnnotationAnnotatedElement:

  • Annotation:是所有注解类型的公共接口。
  • AnnotatedElement:定义了注解解析相关的方法,如下表所示
方法功能描述
<T extends Annotation> T getAnnotation(Class<T> annotationClass)获得当前对象指定的注解,如果没有则返回空。
Annotation[] getAnnotations()返回此元素上存在的所有注解。(包括从父类继承的注解
Annotation[] getDeclaredAnnotations()返回此元素上存在的所有注解。(不包括从父类继承的注解
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)判断当前对象是否有指定的注解,有则返回 true,否则返回 false。

Class, Constructor, Field, Method等都是AnnotatedElement接口的实现类,所以都有上面的方法。

4.2 注解解析案例

注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface MyAnnotation {
    String game();
    String[] Company() default {"Tencent","Roit"};
}

使用注解

@MyAnnotation(game="DOTA2")
public class MyAnnotationTest {

    @MyAnnotation(game="LOL")
    public void play(){
        System.out.println("玩游戏..");
    }
}

解析注解

public class GetAnnotationTest {

    //获取类上的注解
    @Test
    public void getAnnotationOnClass(){
        Class<MyAnnotationTest> clazz = MyAnnotationTest.class;
        if(clazz.isAnnotationPresent(MyAnnotation.class)){
            MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class);
            System.out.println("游戏名:"+annotation.game());
            System.out.println("公司:"+annotation.Company()[0]+"和"+annotation.Company()[1]);
        }
    }
    /* 
    运行结果:
        游戏名:DOTA2
		公司:Tencent和Roit
	*/

    //获取方法上的注解
    @Test
    public void getAnnotationOnMethod() throws NoSuchMethodException {
        Class<MyAnnotationTest> clazz = MyAnnotationTest.class;
        Method playMethod = clazz.getMethod("play");
        if(playMethod.isAnnotationPresent(MyAnnotation.class)){
            MyAnnotation annotation = playMethod.getAnnotation(MyAnnotation.class);
            System.out.println("游戏名:"+annotation.game());
            System.out.println("公司:"+annotation.Company()[0]+"和"+annotation.Company()[1]);
        }
    }
}
   /* 
    运行结果:
        游戏名:LOL
        公司:Tencent和Roit
	*/
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值