java注解

Posted 新火且试茶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java注解相关的知识,希望对你有一定的参考价值。

注解(Annotation)

Java中的注解是相当重要的知识点,在许多框架尤其是SpringBoot框架,使用注解来替代配置文件,很大程度上减少了配置的麻烦。

Java中的类,方法,变量,参数和包都可以被注解标注。然而注解本身并不具有任何逻辑功能,它的存在更像是一个标签,告诉别人应该对它标注的内容进行何种处理。

比如我们常见的注解

  • @Override:检查该方法是否是重写方法
  • @Deprecated:标记过时方法。
  • @SuppressWarnings:指示编译器去忽略注解中声明的警告。

java中注解都实现接口Annotation

package java.lang.annotation;
public interface Annotation {

    boolean equals(Object obj);

    int hashCode();

    String toString();

    Class<? extends Annotation> annotationType();
}

java中注解的定义形式

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}

要理解上面的注解形式,我们先要了解一下元注解

元注解

元注解就是可以注解到注解上的注解,是用来说明注解性质的基本注解。

@Retention

用来指定注解的保留策略,即注解的存活时间

通过枚举类RetentionPolicy的值来指定

package java.lang.annotation;
public enum RetentionPolicy {
    SOURCE,            /*注解只保留在源码阶段,编译器处理完之后就没有该Annotation信息了  */

    CLASS,             /* 编译器将Annotation存储于类对应的.class文件中,但不会加载到JVM中。默认行为  */

    RUNTIME            /* 编译器将Annotation存储于class文件中,可保留到程序运行时,并且可由JVM读入,因此可以通过反射读取它们 */
}

上面所提到的@Override和@SupressWarnings注解的保留策略就是SOURCE,因为只有编写源代码时我们才需要警告,在编译过后便没有必要保留。

常用的保留策略还是RUNTIME,毕竟我们通常需要在运行时对注解标注的内容进行相应的处理。

@Target

我们知道注解可以标注在类、方法、变量等,而@Target就是用来指定注解可以标注的类型。与枚举类型ElementType结合使用。

package java.lang.annotation;

public enum ElementType {
    TYPE,               /* 类、接口(包括注释类型)或枚举声明  */

    FIELD,              /* 字段声明(包括枚举常量)  */

    METHOD,             /* 方法声明  */

    PARAMETER,          /* 参数声明  */

    CONSTRUCTOR,        /* 构造方法声明  */

    LOCAL_VARIABLE,     /* 局部变量声明  */

    ANNOTATION_TYPE,    /* 注释类型声明  */

    PACKAGE,             /* 包声明  */
        
    TYPE_PARAMETER,		 /* 参数类型声明  1.8后新增*/
	
    TYPE_USE			 /*TYPE和TYPE_PARAMETER的结合,1.8后新增 */
}

@Target可有可无,如果没有,若没有则该注解可以标注在任何地方;如果有,则该注解必须标注在指定的地方。

@Documented

类和方法的注解默认情况下是不出现在javadoc中的,使用该@Document修饰的注解,生成javadoc时会出现在javadoc中。

@Inherited

Inherited是继承的意思,如果一个类被用@Inherited标注的注解标注了的话,那么这个子类就会继承这个父类的注解。

例如有一个注解Inheritable被@Inherited标注

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

有一个父类被@Inheritable标注

@Inheritable
class InheritedFather{}

有一个继承父类的子类,这个子类没有用任何注解标注

public class InheritedSun extends InheritedFather{
    public static void main(String[] args) {
        //结果为true
        System.out.println(InheritedSun.class.isAnnotationPresent(Inheritable.class));
    }
}

上面打印结果为true,说明子类继承了父类的注解

@Repeatable

可重复的意思,java1.8后加入的,这个注解说明可以在其标注的同一个地方多次使用这个注解。

例如,您正在编写代码以使用计时器服务,以在每月的最后一天以及每个星期五的晚上11:00运行doPeriodicCleanup方法。代码如下:

声明可重复注解

import java.lang.annotation.Repeatable;

@Repeatable(Schedules.class)
public @interface Schedule {
  String dayOfMonth() default "first";
  String dayOfWeek() default "Mon";
  int hour() default 12;
}

声明容器注解,即可存放注解的注解。容器注解必须有value属性,类型为@Repeatable注解的注解数组

public @interface Schedules {
    Schedule[] value();
}

标注在doPeriodicCleanup方法上

@Schedule(dayOfMonth="last")
@Schedule(dayOfWeek="Fri", hour="23")
public void doPeriodicCleanup() { ... }

注解的属性

如上例子

@Repeatable(Schedules.class)
public @interface Schedule {
  String dayOfMonth() default "first";
  String dayOfWeek() default "Mon";
  int hour() default 12;
}

String为类型,dayOfMonth()为属性名,default指定默认值。

使用形如@Schedule(dayOfWeek="Fri", hour="23")的格式来使用注解。

如果注解只有一个value属性,可以将value省略,直接写值,例如@Repeatable。@Repeatable(Schedules.class)

public @interface Repeatable {
    Class<? extends Annotation> value();
}

,如果注解内无属性,括号也可省略。如@Override

java常用注解

  • @Deprecated -- @Deprecated 所标注内容,不再被建议使用。
  • @Override -- @Override 只能标注方法,表示该方法覆盖父类中的方法。
  • @Documented -- @Documented 所标注内容,可以出现在javadoc中。
  • @Inherited -- @Inherited只能被用来标注“Annotation类型”,它所标注的Annotation具有继承性
  • @Retention -- @Retention只能被用来标注“Annotation类型”,而且它被用来指定Annotation的RetentionPolicy属性。
  • @Target -- @Target只能被用来标注“Annotation类型”,而且它被用来指定Annotation的ElementType属性。
  • @SuppressWarnings -- @SuppressWarnings 所标注内容产生的警告,编译器会对这些警告保持静默。
  • @FunctionalInterface --函数式接口注解,这个是 Java 1.8 版本引入的新特性。函数式接口 (Functional Interface) 就是一个具有一个方法的普通接口。线程开发中常用的 Runnable 就是一个典型的函数式接口,上面源码可以看到它就被 @FunctionalInterface 注解。

反射与注解

上面已经了解了注解的定义与基本知识,注解最重要的便是对其标注的内容进行处理。

下面便进行一个简单实例,使用一个注解标注在方法上,为方法内的参数赋值。

首先,自定义注释

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String[] value();
}

创建一个类

public class Person {
    private String name;
    private String sex;

	//将注释中的值赋给方法
    @MyAnnotation(value = {"张三","男"})
    public void setNameAndSex(String name,String sex){
        System.out.println(name + "," + sex);
    }
}

测试方法

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    Class<Person> clazz = Person.class;
    Person person = new Person();
	//通过反射获取方法
    Method method = clazz.getMethod("setNameAndSex", String.class, String.class);
    //如果方法被@MyAnnotation注解标注
    if (method.isAnnotationPresent(MyAnnotation.class)) {
        //获取这个方法的MyAnnotation注解
        MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
        //获取注解内的属性值
        String[] value = annotation.value();
        //将属性值当做参数执行方法
        method.invoke(person,value[0],value[1]);
    }

}

以上是关于java注解的主要内容,如果未能解决你的问题,请参考以下文章

JAXB的@XmlElement注解

java代码在片段活动中不起作用

Android APT注解处理器 ( 根据注解生成 Java 代码 )

java 代码片段【JAVA】

# Java 常用代码片段

# Java 常用代码片段