前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >快速掌握JDK常用注解

快速掌握JDK常用注解

作者头像
田维常
发布2019-08-28 15:44:51
7050
发布2019-08-28 15:44:51
举报
文章被收录于专栏:Java后端技术栈cwnait

@Target

字面意义为目标。@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。同时@Target只能修饰注解定义。

源码
代码语言:javascript
复制
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

ElementType元素类型有:

代码语言:javascript
复制
public enum ElementType {
    //指定该策略的注解可以修饰类、接口(包括注解类型)或枚举定义
    TYPE,
    //指定该策略的注解只能修饰成员变量的定义
    FIELD,
    //指定该测录的注解只能修饰方法的定义
    METHOD,
    //指定该策略的注解可以修饰参数
    PARAMETER,
     //指定该策略的注解只能修饰构造器
    CONSTRUCTOR,
    //指定该策略的注解只能修饰局部变量
    LOCAL_VARIABLE,
    //指定该策略的注解只能修饰注解
    ANNOTATION_TYPE,
    //指定该策略的注解只能修饰包定义
    PACKAGE,
    //JDK1.8版本开始
    //指定该策略的注解只能修饰参数类型的定义
    TYPE_PARAMETER,
    //JDK1.8版本开始
    //指定该策略的注解只能修饰一个类型的使用
    TYPE_USE
}
示例

如下@Target(ElementType.FIELD)定义了@LogAnnotation只能用于修饰属性字段。

代码语言:javascript
复制
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAnnotation {
        
}
public class MyTest{
    @LogAnnotation
    private String name;
    // get set
    
    public static void main(String[] args) {
        Class cl=MyTest.class;
        Field [] fields=cl.getDeclaredFields();
        Annotation[] aa=   fields[0].getDeclaredAnnotations();
        //下面输出:@com.tian.swagger.annotation.LogAnnotation()
        System.out.println(aa[0]);
    }
}

@LogAnnotation的使用 只能使用在属性字段上。

@Retention

字面意义为保留,维持。其中@Retention只能修饰注解定义,用于指定被修饰的注解可以保留多长时间,包含了一个RetentionPolicy类的value变量,所以使用此注解时必须为该value变量赋值。

如果RetentionPolicy不指定默认为RetentionPolicy.CLASS。

源码
代码语言:javascript
复制
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    RetentionPolicy value();
}

其中RetentionPolicy保留策略有三种:

代码语言:javascript
复制
public enum RetentionPolicy {
    // 注解之保留在源代码中,编译器直接丢弃这种注解
    SOURCE,
    // 编译器将把注解记录仪在class文件中,当运行java程序时,
    //JVM不可获取注解信息,---默认值
    CLASS,
    //编译器将把注解记录在class文件中,当运行java程序时,
    //JVM也可以获取注解信息,程序代码里也可以通过反射获取注解信息
    RUNTIME
}
示例

两种使用方式:

代码语言:javascript
复制
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRetentionAnnotation {
}
代码语言:javascript
复制
@Retention(value = RetentionPolicy.RUNTIME)
public @interface MyRetentionAnnotation {
}

如果使用注解时只需要为value成员变量指定值,则使用该注解时可以直接在该注解后的括号里指定value的值,无须使用value=值的形式。

@Documented

@Ducumented用于指定被该元注解修饰的注解将被javadoc工具提取成文档,如果定义注解类时使用了@Documented修饰,则所有使用该注解修饰的程序元素的API文档中将会包含该注解说明。

源码
代码语言:javascript
复制
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
//表示具有类型的注释默认情况下由javadoc和类似工具记录。应该使用此类型来注释其注释影响其客户端使用注释元素的类型的声明。
//如果类型声明使用Documented进行注释,则其注释将成为注释元素的公共API的一部分。   
}
示例
代码语言:javascript
复制
@Documented //自定义注解 加上注解Documented
@Retention(value = RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyRetentionAnnotation {
}
代码语言:javascript
复制
public class MyTest {
    //使用自定义注解
    @MyRetentionAnnotation
    public String annotation() {
        return super.toString();
    }
}

找到MyTest.java文件,然后再当前目录实行:

javadoc -d doc MyTest.java

doc目录下回生成一对html文件,打开index.html(可能有的需要使用IE浏览器打开),最后生成为如下文档形式:

进入MyTest

看到这文档是不是很熟悉呀, 以下是JDK API文档格式,所以如果需要把自己代码生成API文档这也是最原始的方案,比如swagger等是目前比较流行的。

Spring boot集成swagger构建API文档

@Repeatable

字面意义为可重复,确实是一个可重复的注解,从JDK1.8开始引入的,在需要对同一种注解多次使用时,往往需要借助@Repeatable。

源码
代码语言:javascript
复制
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    /**
     * value 表示可重复注释类型的包含注释类型
     */
    Class<? extends Annotation> value();
}
示例
代码语言:javascript
复制
@Retention(RetentionPolicy.RUNTIME)
public @interface CityAnnotations {
    CityAnnotation [] value();
}
代码语言:javascript
复制
@Repeatable(CityAnnotations.class)
public @interface CityAnnotation {
    String value() default "beijing";
}
代码语言:javascript
复制
@CityAnnotation("上海")
@CityAnnotation("深圳")
@CityAnnotation("广州")
@CityAnnotation("杭州")
public class RepeatableTest {
    public static void main(String[] args) {
        //注意CityAnnotations与CityAnnotation
        if (RepeatableTest.class.isAnnotationPresent(CityAnnotations.class)) {
            CityAnnotations cityAnnotations = RepeatableTest.class.getAnnotation(CityAnnotations.class);
            for (CityAnnotation cityAnnotation : cityAnnotations.value()) {
                System.out.print(cityAnnotation.value()+",");
            }
        }
    }
}
代码语言:javascript
复制
上海,深圳,广州,杭州,

@Override

字面意义为覆盖,@override注释在jdk1.5环境下只能用于对继承的类的方法的重写,而不能用于对实现的接口中的方法的实现。

个人理解@override注释只是起到标记作用,标记着此方法是重写父类的方法。

源码
代码语言:javascript
复制
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
示例
代码语言:javascript
复制
public class UserInfo {
    private Long userId;
    private String userName;

    @Override
    public String toString() {
        return "UserInfo{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                '}';
    }
}

这里的toString不能写成tostring,因为toString方法是UserInfo类从Object中继承来而重写的。

代码语言:javascript
复制
public class Object {
    public String toString() {
        return getClass().getName() + "@" +                       Integer.toHexString(hashCode());    }}

@Deprecated

字面意义为不赞同,不推荐使用。在jdk中同样的,在不建议其他程序员使用的类、方法和字段上,添加@Deprecated注解标示即可。

源码
代码语言:javascript
复制
@Documented
@Retention(RetentionPolicy.RUNTIME)
//适用范围或者说目标类型几乎包括所有,构造函数、属性、方法等
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
示例
代码语言:javascript
复制
    public static void main(String[] args) {
        methodDeprecated();//被一条横线划着
    }
    @Deprecated
    public static void methodDeprecated(){
        System.out.println("此方法不推荐使用");
    }

调用方会出现警告,提示你此方法已经过时请使用最新其他姿势

这样的方法在很多框架和JDK源码中都有使用,表示作者不建议你使用该方法。

比如:java.util.Date中就有使用到

代码语言:javascript
复制
    @Deprecated
    public Date(int year, int month, int date) {
        this(year, month, date, 0, 0, 0);
    }
    @Deprecated
    public Date(String s) {
        this(parse(s));
    }

@SuppressWarnings

@SuppressWarnings该注解的作用是给编译器一条指令,告诉它对被注解的代码元素内部的某些警告保持静默。@SuppressWarnings 可以抑制一些可以通过编译但可能存在运行异常的代码发出警告,确定代码可以运行不会出现警告提示的情况下,可以使用该注解。

  • @SuppressWarning("unchecked"), (未选中) 告诉编译器忽略unchecked警告信息,如使用List,ArrayList等进行参数化<即指定泛型>产生的警告信息。
  • @SuppressWarning("serial"), (序列) 如果编译器出现这个警告信息:The serializable class Pat_userPojo does not declare a static final serialVersionUID field of type long。使用这个注解将这个警告信息去掉。
  • @SuppressWarnings("unchecked", "deprecation") 告诉编译器同事忽略这两个警告信息。
  • @SuppressWarnings(value={"unchecked", "deprecation"}) 等同于@SuppressWarnings("unchecked", "deprecation")
源码
代码语言:javascript
复制
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}
示例
代码语言:javascript
复制
    private void setStr(String str) {
        List list = new ArrayList();
        list.add(str);
    }

上述代码会出现警告,警告消息为:

代码语言:javascript
复制
 Unchecked call to 'add(E)' as a member of raw type 'java.util.List' less... (Ctrl+F1)
Signals places where an unchecked warning is issued by the compiler, for example:

  void f(HashMap map) {
    map.put("key", "value");
  }
  
Hint: Pass -Xlint:unchecked to javac to get more details.

修改上述代码

代码语言:javascript
复制
    @SuppressWarnings("unchecked")
    private void setStr(String str) {
        List list = new ArrayList();
        list.add(str);
    }

然后警告就消失了。同时注意这里的unchecked单词不能写错,否则警告继续。

@SafeVarargs

@SafeVarargs在JDK 1.7中引入,主要目的是处理可变长参数中的泛型,此注解告诉编译器:在可变长参数中的泛型是类型安全的。可变长参数是使用数组存储的,而数组和泛型不能很好的混合使用。

注意:使用@SafeVarargs注解,对于非static或非final声明的方法,不适用,会编译不通过。

示例
代码语言:javascript
复制
public class MyTest<S> {
    @SuppressWarnings("unchecked")
    //@SafeVarargs 会编译不通过
    private void setStr(String str) {
        List list = new ArrayList();
        list.add(str);
        System.out.println(list.get(0));
    }
    @SafeVarargs //不加此注解将会发出警告
    public final void printSelfArgs(S... args){
        for (S arg : args) {
            System.out.println(arg);
        }
    }
}

警告信息

代码语言:javascript
复制
This inspection reports all methods with variable arity which can be annotated as @SafeVarargs. @SafeVarargs annotation suppresses unchecked warnings about parameterized array creation at call sites.
This annotation is not supported under Java 1.6 or earlier JVMs.

切记:一定要能保证运行期不会出问题才标此注解,否则是给自己挖坑。

@Inherited

表示注释类型自动继承。如果在注释类型声明中存在继承的元注释,并且用户在类声明上查询注释类型,并且类声明没有此类型的注释,则该类的超类将自动查询注释类型。将重复此过程,直到找到此类型的注释,或者达到类层次结构(Object)的顶部。如果没有超类具有此类型的注释,则查询将指示所讨论的类没有这样的注释。

代码语言:javascript
复制
注意:如果使用注释类型来注释除类之外的任何内容,则此元注释类型不起作用。另外,这个元注释只会导致从超类继承注释; 已实现的接口上的注释无效。
源码
代码语言:javascript
复制
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
示例
代码语言:javascript
复制
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited//申明为可继承
public @interface MyInherited {
}

把自定义注解标记在父类上

代码语言:javascript
复制
@MyInherited
public class BaseInherited {
}
代码语言:javascript
复制
//子类继承父类
public class InheritedDemo extends BaseInherited {
    public static void main(String[] args) {
    //InheritedDemo上是否有注解Inherited
    System.out.println(
    InheritedDemo.class.isAnnotationPresent(MyInherited.class));
    }
}

运行结果:true

由此可证明子类继承了父类的@MyInherited。

@FunctionalInterface

该注解从JDK1.8引入的,使用的信息注释类型,以指示在接口类型声明旨在是一个功能接口由Java语言规范所定义的。在概念上,功能界面只有一个抽象方法。由于default methods有一个实现,它们不是抽象的。如果接口声明了一个抽象方法覆盖的公共方法之一java.lang.Object ,也不会向接口的抽象方法计数统计以来的接口的任何实施都会有一个实现从java.lang.Object或其他地方。请注意,可以使用lambda表达式,方法引用或构造函数引用创建函数接口的实例。

如果使用此注释类型注释类型,则编译器需要生成错误消息,除非:

类型是接口类型,而不是注释类型,枚举或类。注释类型满足功能界面的要求。

但是,编译器会将符合功能接口定义的任何接口视为功能接口,而不管FunctionalInterface声明是否存在FunctionalInterface注释。

源码
代码语言:javascript
复制
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
示例
代码语言:javascript
复制
@FunctionalInterface
public interface MyFunctionalInterfaceDemo{
    static void methdo1() {
        System.out.println("method1");
    }
    default void method2() {
        System.out.println("method2");
    }
    //只定义了一个抽象方法
    void test();
}

我们编译上面这段代码,可能完全看不出程序中的@FunctionalInterface的作用,因为@FunctionalInterface只是告诉编译器检车这个接口,保证该接口只能包含一个抽象方法,否则编译会报错。@FunctionalInterface主要是帮助程序避免一些低级错误,例如:在上面的MyFunctionalInterfaceDemo的接口中再增加一个抽象方法,编译时就会报错了。

注意:@FunctionalInterface只能修饰接口,不能修饰其他程序元素。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java后端技术栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • @Target
    • 源码
      • 示例
      • @Retention
        • 源码
          • 示例
          • @Documented
            • 源码
              • 示例
              • @Repeatable
                • 源码
                  • 示例
                  • @Override
                    • 源码
                      • 示例
                      • @Deprecated
                        • 源码
                          • 示例
                          • @SuppressWarnings
                            • 源码
                              • 示例
                              • @SafeVarargs
                                • 示例
                                • @Inherited
                                  • 源码
                                    • 示例
                                    • @FunctionalInterface
                                      • 源码
                                        • 示例
                                        领券
                                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档