专栏首页村雨遥深入浅出 Java 注解!

深入浅出 Java 注解!

已收录至博客 https://cunyu1943.blog.csdn.net,建议关注后续修改更新 ~

1注解简介

所谓注解,其实就像一种拥有特定作用的注释,自 JDK1.5 及之后版本所引入的特性,它是放在 Java 源码的类、方法、字段、参数前的一种用作标注的“元数据”,与类、接口、枚举处于同一个层次中。

通过其作用的不同,我们常常将注解分为如下 3 类:

  1. 编写文档:通过代码中标识的注解生成对应文档(即类似于 Java doc 的文档);
  2. 代码分析:通过代码中标识的注解对代码进行分析(使用反射);
  3. 编译检查:通过代码中标识的注解让编译器能实现基本的编译检查(@Override);

2常用的预定义注解

@Override

一般是用在方法上,表示重写该父类的方法,比如我们使用最多的 toString() 方法,它是 Object 类的一个方法,而我们的写的类都是继承自 Object 类,所以我们自定义的所有类都是有 toString() 方法的。但是如果我们自定义类中的方法在父类中没有,则不能使用该注解,否则会导致无法编译通过。

package com.cunyu;

/**
 * Created with IntelliJ IDEA.
 *
 * @author : cunyu
 * @version : 1.0
 * @email : 747731461@qq.com
 * @website : https://cunyu1943.github.io
 * @date : 2021/6/20 10:04
 * @project : JavaWeb
 * @package : com.cunyu
 * @className : OverrideTest
 * @description :
 */

public class OverrideTest {
    private Integer id;
    private String name;

    public OverrideTest(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("OverrideTest{");
        sb.append("id=").append(id);
        sb.append(", name='").append(name).append('\'');
        sb.append('}');
        return sb.toString();
    }

    public static void main(String[] args) {
        Integer id = 101;
        String name = "村雨遥";
        OverrideTest overrideTest = new OverrideTest(id, name);

        System.out.println(overrideTest);
    }
}

@Override

@Deprecated

一般用在方法之前,表示该方法已经过期,不建议再继续使用(但是仍然有效,只不过可能有更新的版本,推荐使用更新的版本)。

package com.cunyu;

/**
 * Created with IntelliJ IDEA.
 *
 * @author : cunyu
 * @version : 1.0
 * @email : 747731461@qq.com
 * @website : https://cunyu1943.github.io
 * @公众号 : 村雨遥
 * @date : 2021/6/20 10:07
 * @project : JavaWeb
 * @package : com.cunyu
 * @className : DeprecateTest
 * @description :
 */

public class DeprecateTest {
    @Deprecated
    public static void sayHello() {
        System.out.println("Hello World!");
    }

    public static void newSayHello() {
        System.out.println("Hello,Welcome to Java !");
    }

    public static void main(String[] args) {
        sayHello();
        newSayHello();
    }
}

@SuppressWarnings

表示忽略警告信息,常用的值以及含义如下表:

描述

deprecation

使用了不赞成使用的类或方法时的警告

unchecked

使用了未经检查的转换时的警告

fallthrough

当 switch 程序块直接通往下一种情况而没有 break 时的警告

path

在类路径、源文件路径等中有不存在的路径时的警告

serial

当在可序列化的类上缺少 serialVersionUID 定义时的警告

finally

任何 finally 子句不能正常完成时的警告

rawtypes

泛型类型未指明

unused

引用定义了,但是没有被使用

all

关闭以上所有情况的警告

package com.cunyu;

import java.util.ArrayList;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 *
 * @author : cunyu
 * @version : 1.0
 * @email : 747731461@qq.com
 * @website : https://cunyu1943.github.io
 * @公众号 : 村雨遥
 * @date : 2021/6/20 10:07
 * @project : JavaWeb
 * @package : com.cunyu
 * @className : SuppressWarningsTest
 * @description :
 */

public class SuppressWarningsTest {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        String item = "村雨遥";
        @SuppressWarnings("rawtypes")
        List items = new ArrayList();
        items.add(item);

        System.out.println(items);
    }
}

3自定义注解

格式

我们可以使用 @interface 来自定义注解,其格式如下:

public @interface AnnotationName{
    // 属性列表
    ……
}

一个简单的示例如下,其中 AnnoDemo 代表着我们自定义注解的名称,而 name()age()score() 则分别表示自定义注解的三个属性,而且我们利用关键字 default 对每个属性都赋予了默认值。

public @interface AnnoDemo{
    String name() default "村雨遥";
    int age() default 20;
    float score() default 60.0f;
}

原理

注解本质上相当于一个接口,它默认继承自 java.lang.annotation.Annotation

public interface AnnotationName extends java.lang.annotation.Annotation{}

参数

注解的参数类似于无参的方法,通常我们推荐用 default 来设定一个默认值,对于方法的基本要求通常有如下几点:

  1. 方法的返回值类型不可以是 void
  2. 如果定义了方法,那么在使用时需要给方法进行赋值,赋值的规则如下:
    1. 若定义方法时,使用了关键字 default 对方法赋予了默认初始值,那么在使用注解时,可以不用对方法进行再次赋值;
    2. 若只有一个方法需要赋值,且方法名为 value,那么此时 value 可以省略,直接定义值即可;
    3. 数组赋值时,值需要用大括号 {} 包裹,若数组中只有一个值,那么此时 {} 可以省略;
public @interface AnnoDemo{
    String name() default "村雨遥";
    int age() default 20;
    float score() default 60.0f;
}

如上述例子中,name()age()score() 就是我们自定义注解的参数。而当我们要是用该注解时,则通过如下方式来对参数进行赋值。

@AnnoDemo(name = "村雨遥", age = 26, score = 95.0f)
public class Demo{
    ……
}

4元注解

定义

所谓元注解(meta annotation),就是可以用来修饰其他注解的注解。

常用的元注解

  1. @Target

描述注解所修饰的对象范围,其取值主要有如下几种:

说明

ElementType.TYPE

表示可以作用于类或接口

ElementType.FIELD

表示可以作用于成员变量

ElementType.METHOD

表示可以作用于方法

ElementType.CONSTRUCTOR

表示可以作用于构造方法

ElementType.PARAMETER

表示可以作用于方法的参数

@Target(ElementType.TYPE)
public @interface AnnoDemo{
    String name() default "村雨遥";
    int age() default 20;
    float score() default 60.0f;
}
  1. @Retention

用于约束注解的生命周期,其取值如下:

说明

RetentionPolicy.SOURCE

表示在源代码文件中有效,注解将被编译器丢弃(注解信息仅保留在源码中,源码经编译后注解信息丢失,不再保留到字节码文件中)

RetentionPolicy.CLASS

表示在字节码文件中有效,注解在字节码文件中可用,但会被 JVM 丢弃

RetentionPolicy.RUNTIME

表示在运行时有效,此时可以通过反射机制来读取注解的信息

@Target(ElementType.TYPE)
@Retention(RetentionPoicy.RUNTIME)
public @interface AnnoDemo{
    String name() default "村雨遥";
    int age() default 20;
    float score() default 60.0f;
}
  1. @Documented

描述其他类型的注解是否被抽取到 API 文档中。

@Target(ElementType.TYPE)
@Retention(RetentionPoicy.RUNTIME)
@Documented
public @interface AnnoDemo{
    String name() default "村雨遥";
    int age() default 20;
    float score() default 60.0f;
}
  1. @Inherited

这是一个标记注解,描述某个注解能够被子类继承,但是该元注解只适合已经配置了 @Target(ElementType.TYPE) 类型的自定义注解,而且仅针对于类的继承,而对于接口的继承则无效。

@Inherited
public @interface AnnoDemo{
    String name() default "村雨遥";
    int age() default 20;
    float score() default 60.0f;
}
  1. @Repeatable

该注解是从 JDK1.8 新引入的元注解,表示在同一位置能够重复相同的注解。在没有该注解之前,我们一般是无法在同一类型上使用相同注解的,但引入该注解后,我们就可以在同一类型上使用相同注解。

@Target(ElementType.TYPE)
@Repeatable(AnnoDemos.class)
public @interface AnnoDemo{
    String name() default "村雨遥";
    int age() default 20;
    float score() default 60.0f;
}

public @interface AnnoDemos{
 AnnoDemo[] value();
}

利用 @Repeatable 配置自定义注解之后,我们就可以在某个类型声明处添加多个我们自定义的注解了。

@AnnoDemo(name = "村雨遥", age = 26, score = 88.0f)
@AnnoDemo(name = "晓瑜", age = 27, score = 90.0f)
public class Student{
    ……
}

5总结

总结上述的知识点,我们将自定义注解的过程归纳为如下 3 步。

  1. 定义一个注解
public @interface AnnoDemo{
}
  1. 添加参数并设置默认值
public @interface AnnoDemo{
    String name() default "村雨遥";
    int age() default 20;
    float score() default 60.0f;
}
  1. 利用元注解来配置我们的自定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnoDemo{
    String name() default "村雨遥";
    int age() default 20;
    float score() default 60.0f;
}

在实际应用过程中,利用元注解配置自定义注解时,必须设置 @Target@Retention 两个元注解,而且 @Retention 的值通常是设置为 RetentionPolicy.RUNTIME

好了,以上就是我们注解的相关概念以及自定义注解所需要的掌握的一些知识点了,如果你觉得对你有所帮助,那就来一波点赞关注吧!

本文分享自微信公众号 - 村雨遥(cunyu1943),作者:村雨遥

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-06-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 深入浅出Java注解

    注解对于开发人员来讲既熟悉又陌生,熟悉是因为只要你是做开发,都会用到注解(常见的@Override);陌生是因为即使不使用注解也照常能够进行开发...

    open
  • Java注解深入浅出(一)-注解的真实面貌

    这处图片引自老罗的博客。为了避免不必要的麻烦,首先声明我个人比较尊敬老罗的。至于为什么放这张图,自然是为本篇博文服务,接下来我自会说明。好了,可以开始今天的博文...

    Anymarvel
  • 深入浅出依赖注入

    或许您已经在项目中已经使用过「依赖注入」,只不过由于某些原因,致使您对它的印象不是特别深刻。

    柳公子
  • 浅入深出Vue:注册

    基本布局已经有了, 现在我们来开始做我们的注册页面~ 当然需要注册才能发表文章啊(糟老头子坏得很, 我可以只有我一个人能发啊)。

    若羽
  • java lambda 深入浅出

    标注为@FunctionalInterface的接口是函数式接口,该接口只有一个自定义方法。注意,只要接口只要包含一个抽象方法,编译器就默认该接口为函数式接口。

    luoxn28
  • 深入浅出Java反射

    反射,它就像是一种魔法,引入运行时自省能力,赋予了 Java 语言令人意外的活力,通过运行时操作元数据或对象,Java 可以灵活地操作运行时才能确定的信息。

    luoxn28
  • 深入浅出学Java-HashMap

    HashMap在JDK1.8之前的实现方式 数组+链表,但是在JDK1.8后对HashMap进行了底层优化,改为了由 数组+链表+红黑树实现,主要的目的是提高查...

    Java架构师必看
  • 深入浅出java虚拟机

    我们都知道,当虚拟机执行Java代码的时候,首先要把字节码文件加载到内存,那么这些类的信息都存放在内存中的哪个区域呢?当我们创建一个对象实例的时候,虚拟机要为...

    烂猪皮
  • VPC 深入浅出解析

    概述 今天给大家介绍一下博主最近做的一个项目:VPC.VPC(Virtual Private Cloud)虚拟私有云,租户可以在云中预置一个逻辑隔离分区,自己定...

    BrianLv
  • 深入浅出理解SerDes

    我们平时使用的I2C、串口等其实都是串行总线,但是因为他们速度较低、时序简单,所以很少在高速串行总线时被提及。但是在高速时代的今天,一些高速总线,如LVDS、M...

    碎碎思
  • 【Java】深入浅出Java回调原理

    首先在Eclipse中新建一个Java项目:CallBackDemoInJava;

    瑞新
  • 深入理解 Java 注解

    从本质上来说,注解是一种标签,其实质上可以视为一种特殊的注释,如果没有解析它的代码,它并不比普通注释强。

    静默虚空
  • 深入浅出 RPC - 浅出篇

    近几年的项目中,服务化和微服务化渐渐成为中大型分布式系统架构的主流方式,而 RPC 在其中扮演着关键的作用。在平时的日常开发中我们都在隐式或显式的使用 RPC,...

    Java高级架构
  • 深入浅出理解闭包

    本篇博客转载自@王福朋 王老师的系列文章。系列文章共计18篇,主要涉及js中的两个重难点—-原型和闭包。由于原型部分我在另外一篇博客有介绍,所以这里只集合了他关...

    Chor
  • 《深入浅出Node.js》-理解Buffer

    Buffer 是一个像 Array 的对象,主要用来操作字节。Buffer 是一个典型的 JavaScript 与 C++ 结合的模块,它将性能相关的部分用 C...

    李振
  • 浅入浅出 Java ConcurrentHashMap

    HashMap 是 Java 中非常强大的数据结构,使用频率非常高,几乎所有的应用程序都会用到它。但 HashMap 不是线程安全的,不能在多线程环境下使用,该...

    沉默王二
  • 深入浅出 Java 中的包装类

    前阵子,我们分享了《Java中的基本数据类型转换》这篇文章,对许多粉丝还是有带来帮助的,今天讲一下 Java 包装类的的由来,及自动装箱、拆箱的概念和原理。

    Java技术栈
  • 深入浅出 Java CMS 学习笔记

    带着问题去学习一个东西,才会有目标感,我先把一直以来自己对CMS的一些疑惑罗列了下,希望这篇学习笔记能解决掉这些疑惑,希望也能对你有所帮助。

    纯洁的微笑
  • 深入浅出 Java 8 Lambda 表达式

    摘要:此篇文章主要介绍 Java8 Lambda 表达式产生的背景和用法,以及 Lambda 表达式与匿名类的不同等。

    九州暮云

扫码关注云+社区

领取腾讯云代金券