Java语法基础: Annotation注解 (总)

注解的相关概念:
注解出现的版本:jdk1.5
Annotation(注解):其实代码中的特殊标记,可以在编译、类加载、运行被读取,并通过相应的处理。通过使用Annotation,程序可与在不改变原有逻辑的情况下,在源文件嵌入一些补充信息。代码分析工具、开发工具、和部署工具可以通过这培训补充信息进行验证或者部署。

常用的框架Spring就是在使用到了上面的原理,通过注解@Controller、@Service和@RequestMapping等注解处来在编译、类加载、运行时通过反射对使用了注解的类或者方法进行相应的处理,实现对应的功能,这样就可以在不改变原有逻辑的情况下,在源文件嵌入一些补充信息和拓展一些功能。

注解的作用:用于为程序元素(类、方法、属性等)设置元数据。

1、jdk提供三个基础注解

基本的Annotation:
Java提供的基本的Annotation:

  • @Override
  • @Deprecated
  • @SuppressWarnings

1.1限定重写父类方法:@Override

限定重写父类方法:@Override
用于指定方法覆盖的,可以强制一个子类必须要覆盖父类的方法。
例如:我们经常覆盖的toString()方法

@Override注解的定义:
在这里插入图片描述

从上面定义的**@Override的来看,该注解用来指定方法重写的,可以强制一个子类必须覆盖父类**的方法。在方法上适用 @Override Annotation的作用是告诉编译器检查该方法,如果父类或者接口中没有这个方法就会报错。可以避免我们在写的时候发生错误。

// 子类
public class SubClass extends SuperClass {
    
    
	// 这里标注了该方法是重写父类的info方法
	// 如果不小心写错成了inf0,加上@Override注解,就会在编写阶段报错
	@Override
	public void info(){
    
    
		System.out.println("SubClass");
	}
}
// 父类
class SuperClass{
    
    
	public void info(){
    
    
		System.out.println("SuperClass");
	}
}

在IDE工具中,如果使用了@Override注解,而父类没有该方法,则会报错,可以避免在覆盖父类方法的时候出现一些错误。

在这里插入图片描述

1.2、标示已经过时:@Deprecated

用于表示某个类(某个方法)已经过时,当使用这些过时的类或者方法的时候,IDE编辑器就会发出警告
@Deprecated 的定义
在这里插入图片描述
用于表示某个程序元素(类、方法等)已过时,但其他程序使用已过时的类、方法的时候,编译器会发出警告。

public class AnnotationTest{
    
    

    public static void main(String[] args) {
    
    
        AnnotationTest test = new AnnotationTest();
        test.deprecatedMethod();
    }

    @Deprecated   // 使用 @Deprecated 来标注该方法已经过时间了
    public void deprecatedMethod(){
    
    
        System.out.println("我已经过时了");
    }
}

上面的编写代码的时候,编译器会发出警告,但是该方法还是可以正常执行的
在这里插入图片描述

注意:@Deprecated Annotation的作用于文档注释中的**@deprecated** 标记的作用基本一样,但用法不同,前者是jdk1.5之后才出现的注解,是直接用于接口方法等上面的。

1.3、抑制编译器警告:@SupperssWarnings

指示被改注解标识的程序元素(以及在该程序中所有子元素)取消指定的编译器警告。

用于取消显示指定的编译器警告,value的值可以是:

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

在这里插入图片描述

在这里插入图片描述
备注:其实我在这里测试了 “unchecked”,但是无法消除泛型异常,无论是IDEA还是eclipse都是一样
IDEA中的测试

eclipse中的测试

2、自定义Annotation

2.1、定义简单的注解

定义新的Annotation类型使用**@interface** 关键字字,它用于定义新的Annotation类型(与定义一个接口非常相似)。
实例1:定义一个简单的注解

// 定义一个非常简单的Annotation类型 Test
public @interface MyTest{
    
    
	// 本注解是不含有属性的
}

使用自定义的注解类型:@MyTest

  1. 使用自定的注解修饰类
// 使用@Test 修饰类
@MyTest
public class ExampleClass{
    
    
}
  1. 使用自定义的注解修饰方法
public class UseInMethodExample(){
    
    
	@MyTest //使用@Test 修饰方法
	public void methodExample(){
    
    
	}
}

2.2、定义带成员变量的注解

在定义Annotation的时候还可带成员变量,成员变量在Annotation定义中以参数方法的形式来声明,其方法名返回值定义了该成员的名字类型

在上我们定义的Annotation可用于修饰类、接口、方法、成员属性等任何程序元素,但是在运行时是获取不到我们自定义的注解的,这是因为

  1. 没有使用的注解@Target,默认是可用于修饰类、接口、方法、成员属性等任何程序元素
  2. 没有使用的注解@Retention,默认的是RetentionPolicy.CLASS(只好保留在class中,运行时不保留)

注解中我们也可以为注解设置一定的属性,例如我们可以在MyAnnoation注解中加入需要的属性(成员变量)

  • 语法: 类型 属性名()
    可以使用default关键之给成员属性设置默认值,在使用的时候可以进行修改。
// 定义有成员属性的注解Person
public @interface Person{
    
    
	//在注解中定义了两个成员属性
	// 属性名:name ,类型:String 
	String name() default "";// 默认空字符串,可以再使用的时候进行修改
	// 属性名:age,类型:Integer
	Integer age();
}

使用@Person

public class Student{
    
    
	@Person(name="Xxx",age=18) // 这里将使用时候,将name改为“Xxx”
	public void info(){
    
    
	//代码实现...
	}
}

2.3、提取Annotation的信息:

在Java中提供了java.lang.reflect.AnnotateElement接口,该接口代表程序中可以接受注释的程序元素,实现类有:

  • java.lang.reflect.Class:类定义
  • java.lang.reflect.Constructor: 构造器定义
  • java.lang.reflect.Field: 类的成员变量定义
  • java.lang.reflect.Method:类的方法定义
  • java.lang.reflect.Package:类的包定义

主要的方法:

  • Annotation[] getAnnotations():获取该程序元素上的所有注解
  • Annotation[] getDeclaredAnnotations():获取该程序元素上的所有运行时的注解
  • default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注解,存在返回true,否返回false

示例:

注意:下面的注解只能获取运行时存在的注解,如果不是的话,就无法获取到,如果时要获取到 @MyAnnotation 注解,需要将注解修改为:

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    
    
	// ...
}
package learn.demo.annotations;

import org.junit.Test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

@SuppressWarnings(value = "unchecked")
public class TestAnnotation {
    
    

    /**
     * 测试获取方法 testGetAnnotation() 上的注解
     * */
    @MyAnnotation
    @Deprecated
    @Test
    public void testGetAnnotation()throws Exception{
    
    
        Class<TestAnnotation> thisClass = TestAnnotation.class;
        Method testGetAnnotation = thisClass.getMethod("testGetAnnotation");
        Annotation[] annotations = testGetAnnotation.getDeclaredAnnotations();
        System.out.println("使用到的注解有"+annotations.length +"个:");
        for (Annotation an: annotations){
    
    
            System.out.println(an);
        }
    }

运行结果:
在这里插入图片描述

3、Meta Annotation(元注解)

元注解:用于修饰注解的注解。
jdk提供了3个基本的Annotation之外,在java.lang.annotation 包中提供了四个Meta Annotation(元注解)。

  1. @Retentoin
  2. @Target
  3. @Documented
  4. @Inherited

3.1、 元注解之@Retention

jdk中**@Retention注解的定义
在这里插入图片描述
@Retention中的成员变量的名称是 value,类型是
RetentionPolicy**枚举类,下面是枚举类的定义

package java.lang.annotation;

public enum RetentionPolicy {
    
    
    /**
     * Annotations are to be discarded by the compiler.
     * 编译器会丢弃掉这类注解
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     * 这类注解会保存在class文件中,运行时,JVM不再保留这类注释,是默认值
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     * @see java.lang.reflect.AnnotatedElement
     *  这类注解会保存在class文件中,运行时,JVM会保留这类注释,可以通过反射获取这类注解
     */
    RUNTIME
}

使用@Retention:
@Retention 只能用于修饰一个Annotation定义,指定该注解可以保存的时间
vaule的成员变量及其含义

  • RetentionPolicy.CLASS:编译器将注解保留在class文件中,但是java程序运行的时候是不加载注释的,JVM是不存在这类注释的,这也是默认值
  • RetentionPolicy.RUNTIME:编译器将注释保留在class文件中,运行的时候,JVM也会去加载这类注释,可以通过反射来获取到。
  • RetentionPolicy.SOURCE:编译器直接丢弃这类注解。
    示例:
@Retention(RetentionPolicy.RUNTIME)
public @interface ExampleAnnotation{
    
    
	//元注解@Retention只能修饰注解,如果修饰其它会报错
}

3.2、 元注解之@Target

@Target也是用于修饰一个Annotation定义,指定被修饰的Annotation能用于修饰哪些程序元素。

@Target元注解的定义
在这里插入图片描述
@Targetvalue成员变量的类型ElementType枚举类

  • ElementType.TYPE:该策略的Annotation只能修饰类、接口(包含注解类型)或者枚举。
  • ElementType.FIELD:该策略的Annotation只能修饰成员变量。
  • ElementType.METHOD:该策略的Annotation只能修饰方法。
  • ElementType.PARAMETER:该策略的Annotation只能修饰参数。
  • ElementType.CONSTRUCTOR:该策略的Annotation只能修饰构造函数。
  • ElementType.LOCAL_VARIABLE:该策略的Annotation只能修饰局部变量。
  • ElementType.ANNOTATION_TYPE:指定该策略的注解只能修饰Annotation(注解)。
  • ElementType.PACKAGE:该策略的包只能修饰包定义。
  • ElementType.TYPE_PARAMETER:可以去修饰泛型的类型(jdk1.8之后的新特性 类型注解)。
  • ElementType.TYPE_USE:表示该注解能写在使用类型的任何语句中(jdk1.8之后的新特性 类型注解)。

@Targetvalue成员变量的类型ElementType枚举类,下面是ElementType的定义:

public enum ElementType {
    
    
    /** Class, interface (including annotation type), or enum declaration
	 * 可用于修饰类、接口(注解)、或者枚举
	 */
    TYPE,

    /** Field declaration (includes enum constants) 
    * 只能修饰成员变量
    */
    FIELD,

    /** Method declaration 
    * 只能用于修饰方法
    */
    METHOD,

    /** Formal parameter declaration 
    *只能用于修饰参数
    */
    PARAMETER,

    /** Constructor declaration 
	* 只能用于修饰构造器
	*/
    CONSTRUCTOR,

    /** Local variable declaration 
    * 只能用于修饰局部变量
    */
    LOCAL_VARIABLE,

    /** Annotation type declaration 
    * 只能用于修饰注解(Annotation)
    */
    ANNOTATION_TYPE,

    /** Package declaration 
    * 	只能用于修饰包
    */
    PACKAGE,

    /**
     * Type parameter declaration
     *	类型参数申明
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     * 类型的使用
     * @since 1.8
     */
    TYPE_USE
}

3.3、 元注解之@Documented

@Documented注解的定义:
在这里插入图片描述
用于指定Meta Annotation 修饰的Annotation类将被javadoc工具提取成文档。
接下来定义一个实例来说明:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
@Documented // 定义一个 TestDocmented 注解,使用@Documented修饰
public @interface TestDocmented{
    
    
}

定义一个TestExample类,使用@TestDocmented

public class TestExample{
    
    
	@TestDocmented
	public void test(){
    
    
		System.out.printlf("测试 @Documented 注解")
	}
}

3.4、 元注解之@Inherited

@Inherited注解的定义
在这里插入图片描述
使用@Inherited:
@Inherited指定被它修饰的注解具有继承性,例如某个类使用了一个被@Inherited的注解,则该类的子类也将继承该注解。
接下来用一个定义来说明

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
@Inherited // @Inherited 修饰
public @interface InheritedTest{
    
    
	// ...
}
// 父类
@InheritedTest //父类使用@InheritedTest
class Base{
    
    
	//...
}
// 子类
public class SubClass extends Base{
    
    
	// ...
}
	/**
     * 测试 @InheritedTest 注解
     * */
    @Test
    public void TestInherited(){
    
    
        Annotation[] baseAns = Base.class.getAnnotations();
        System.out.println("Base 类使用的注解");
        for (Annotation an: baseAns ){
    
    
            System.out.println(an);
        }
		System.out.println(SubClass.class.isAnnotationPresent(@InheritedTest));
        System.out.println("SubClass 类使用的注解");
        Annotation[] subClassAns = SubClass .class.getAnnotations();
        for (Annotation an: subClassAns ){
    
    
            System.out.println(an);
        }
       
    }

JDK8的新特性
可重复注解:@Repeatable注解
保证两个注解的@Target 和@Retention的注解的属性值必须一致。
@Repeatable在jdk中的定义
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Hicodden/article/details/106059057