千家信息网

如何理解注解

发表于:2025-02-01 作者:千家信息网编辑
千家信息网最后更新 2025年02月01日,这篇文章主要讲解了"如何理解注解",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"如何理解注解"吧!本文主要内容如下:背景现在已经处于注解盛行时代,注解@
千家信息网最后更新 2025年02月01日如何理解注解

这篇文章主要讲解了"如何理解注解",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"如何理解注解"吧!

本文主要内容如下:

背景

现在已经处于注解盛行时代,注解@Override ,这个注解是再熟悉不过了,还有@Controller、@RequestMapping、@Service.....

注解已经是作为一个开发中必备的技能了。

如果在面试中被问到注解,说不出个123,就只能回去等通知了。

什么是注解?

注解annotation是JavaSE5.0中新增功能。可以理解为注解是一种标记,这种标记可以在编译、类加载、运行时被读取,并执行相应的处理。

它可以添加到程序的任何元素上:包声明、类型声明、构造方法、普通方法、成员变量、参数。

注解的老大:

package java.lang.annotation;    //是个接口    public interface Annotation {            boolean equals(Object obj);        int hashCode();         String toString();         //获取注解类型        Class annotationType();    }

JDK自带为我们提供了元注解,下面就来聊聊JDK的元注解。

元注解有哪些?

JDK为我们提供五个元注解,位于java.lang.annotation 包目录下。分别为:

  • @Retention

  • @Target

  • @Documented

  • @Inherited

  • @Repeatable

Retention注解

字面翻译:

该注解就是定义该注解是作用于什么阶段。

编译、类加载、运行(使用最多)

注解源码:

@Documented    @Retention(RetentionPolicy.RUNTIME)    @Target(ElementType.ANNOTATION_TYPE)    public @interface Retention {        /**         * Returns the retention policy.         * @return the retention policy         */        RetentionPolicy value();    }

保留策略

package java.lang.annotation;    public enum RetentionPolicy {         SOURCE,         CLASS,         RUNTIME    }

SOURCE:只在Java源代码中,编译器编译的时候会把它直接丢弃。

CLASS:编译器将注解记录在.class文件中。当运行Java程序时,JVM不能获取注解信息。这是默认值。

RUNTIME:编译器将注解记录在.class文件中。当运行Java程序时,JVM也能获取注解信息。程序可以通过反射获取注解信息。

注意:如果使用该注解的时候必须指定value的值。

Target注解

字面意义为目标,下面来看看器源码:

@Documented    @Retention(RetentionPolicy.RUNTIME)    @Target(ElementType.ANNOTATION_TYPE)    public @interface Target {        //元素类型        ElementType[] value();    }    public enum ElementType {         TYPE,         FIELD,         METHOD,         PARAMETER,         CONSTRUCTOR,         LOCAL_VARIABLE,         ANNOTATION_TYPE,         PACKAGE,         /** @since 1.8*/        TYPE_PARAMETER,        /** @since 1.8*/        TYPE_USE    }

Target注解是在声明创建一个注解的时候,指示该注解可以作用于程序中的哪些元素。

它里边也包含一个名为value的成员变量,value成员变量的值有如下几种

  • ANNOTATION_TYPE:指定当前注解只能修饰其它注解

  • CONSTRUCTOR:指定当前注解只能修饰构造方法

  • FIELD:指定当前注解只能修饰成员变量

  • LOCAL_VARIABLE:指定当前注解只能修饰局部变量

  • METHOD:指定当前注解只能修饰方法

  • PACKAGE:指定当前注解只能修饰包

  • PARAMETER:指定当前注解只能修饰参数

  • TYPE:指定当前注解可以修饰类,接口,其它注解,枚举等类型

比如说:常用的Spring的注解@Controller,是用来作用于类上的。

Documented 注解

字面意义就是文档。@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文 档化。Documented是一个标记注解,没有成员 。

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

主要是用来生成文档的,工作中基本上很少使用,作为了解就可以了。

Inherited 注解

字面意义:

@Inherited注解是在声明创建一个注解的时候,指定该注解将具有继承性。

Inherited 源码:

@Documented    @Retention(RetentionPolicy.RUNTIME)    @Target(ElementType.ANNOTATION_TYPE)    public @interface Inherited {    }

如果某个类使用了该注解@Xx,则其子类也将自动被此注解@Xx所修饰。

使用模板:

@Retention(RetentionPolicy.RUNTIME)    @Target(ElementType.TYPE)    @Inherited    public @interface A {            }    @A    public class Base {            }    //Sub也带有@A    public class Sub extends Base {    }

Repeatable注解

@Repeatable元注解,顾名思义,重复注解,就是在声明创建注解的时候,指定该注解可以被同一个程序元素多次使用。这是JDK8新增的注解,重复注解只是一种简单化写法,这种简单化写法是一种假象。多个重复注解其实会被作为"容器"注解的value成员变量的数组元素。

比如下面Spring中注解ComponentScan:

@Retention(RetentionPolicy.RUNTIME)    @Target(ElementType.TYPE)    @Documented    @Repeatable(ComponentScans.class)    public @interface ComponentScan {    }        @Retention(RetentionPolicy.RUNTIME)    @Target(ElementType.TYPE)    @Documented    public @interface ComponentScans {     ComponentScan[] value();    }

使用案例

import org.springframework.context.annotation.Bean;    import org.springframework.context.annotation.ComponentScan;    import org.springframework.context.annotation.ComponentScans;        @ComponentScans(value = {            @ComponentScan(value = "com.tian.pakage0"),            @ComponentScan(value = "com.tian.pakage1")})    public class MainConfig {        @Bean        public User person() {            return new User();        }    }

基本注解

注解必须使用工具来处理,工具负责提取注解中包含的元数据,工具还会根据这些元数据增加额外的功能。下面我们先来了解一下5个基本注解的用法。

  • Override

  • SafeVarargs

  • SuppressWarnings

  • FunctionalInterface

  • Deprecated

下面我们就来说说这五个注解。

Override

用于标识方法,标识该方法属于重写父类的方法 。

//只能用于方法上    @Target(ElementType.METHOD)    //源码中编译时就会使用    @Retention(RetentionPolicy.SOURCE)    public @interface Override {    }

此注解表面是重写父类的方法,只能用于方法上,并且用于编译阶段,我们在开发的时候,如果注解使用不当,在源码编译时立马就会做出提示。

SafeVarargs

@SafeVarargs注解是在JDK7中引入的。此注解适用于接受varargs参数的final和static方法或构造函数。此注解用于确保方法不会对其varargs参数执行不安全的操作。从Java9开始,@SafeVarargs注解也适用于私有实例方法。

@SafeVarargs源码

@Documented    @Retention(RetentionPolicy.RUNTIME)    @Target({ElementType.CONSTRUCTOR, ElementType.METHOD})    public @interface SafeVarargs {}

使用案例

如果不使用此注解

public class SafevarargsTest {    public static void main(String[] args) {        // 传递可变参数,参数是泛型集合        display(10, 20, 30);        // 传递可变参数,参数是非泛型集合        display("10", 20, 1000L); // 会有编译警告    }    public static  void display(T... array) {        for (T arg : array) {            System.out.println(arg.getClass().getName() + ":" + arg);        }    }}

display方法会提示

如果我们给这个方法添加此注解后

上面的代码在可变参数display前添加了@SafeVarargs注解,当然也可以使用 @SuppressWarnings("unchecked") 注解,可是,两者相比较来说的话@SafeVarargs注解更适合。

注意:@SafeVarargs注解不适用于非static或非final声明的方法,对于未声明为static或final的方法,假如,要抑制unchecked警告,可以使用@SuppressWarnings注解。

SuppressWarnings

用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告。

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})    @Retention(RetentionPolicy.SOURCE)    public @interface SuppressWarnings {        String[] value();    }

编译时期就会做出提示,使用范围也是比较广泛,可以适用于类、接口、枚举、方法、字段等。

我们开发过程中见过最多的应该是集合存放数据的时候,比如下面的例子。

但是如果我们把第一行代码注释放开。

这样就不会提示了。

FunctionalInterface

JDK8新增注解。@FunctionalInterface标记在接口上,"函数式接口"是指仅仅只包含一个抽象方法的接口。

源码

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

1、该注解只能标记在"有且仅有一个抽象方法"的接口上。

2、JDK8接口中的静态方法和默认方法,都不算是抽象方法。

3、接口默认继承java.lang.Object,所以如果接口显示声明覆盖了Object中方法,那么也不算抽象方法。

4、该注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错。

5、@FunctionalInterface 注解的interface。它的特点是其中只有一个子类必须要实现的abstract方法。

使用场景

Callable、Runnable等就有使用到。

@FunctionalInterface    public interface Callable {        V call() throws Exception;    }    @FunctionalInterface    public interface Runnable {        public abstract void run();    }

Deprecated

用于标识方法或类,标识该类或方法已过时,建议不要使用 。

@Documented    @Retention(RetentionPolicy.RUNTIME)    @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})    public @interface Deprecated {    }

使用范围就想到广泛了,构造方法、字段、方法等。

注意:只是提示过时了,不建议使用,不代表不能用,但是我们如果想用某个使用此注解标记的方法或者类的时候,建议找找有没有替换方案,实在没有替换方案,搞清楚为什么它会被设置成过时,使用不当可能会对我们的程序造成你意想不到问题,也可能会挖坑。

自定义注解

终于来到自定义了,下面我们来自定义一个注解。

使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。

格式

public @interface 注解名 {定义体}

案例

自定义我们的注解

@Documented    @Retention(RetentionPolicy.RUNTIME)    @Target(ElementType.METHOD)    public @interface  MyInterface {        String value() default "";    }

使用阶段为运行阶段,目标是在方法上。定义一个属性value默认值为空字符串。

下面我们就来使用我们定义的注解。

import java.lang.reflect.Method;        public class InterfaceDemo {            //@MyInterface("老田自定义的注解")         //@MyInterface        @MyInterface(value = "老田自定义的注解")        public void test() {            // TODO:        }            public static void main(String[] args) {            InterfaceDemo interfaceDemo = new InterfaceDemo();            Class clazz = interfaceDemo.getClass();            Method[] methods = clazz.getMethods();            for (Method method : methods) {                if ("test".contentEquals(method.getName())) {                    System.out.println("方法名称= ">

如果没有指定value的值,那就使用默认值,value两种方式赋值:

@MyInterface("老田自定义的注解")    @MyInterface(value = "老田自定义的注解")

然后我们运行上面的代码,输出结果为:

以上我们就搞定了自定义注解以及使用,获取属性值。

注解是普通类还是接口?

使用javap查看我们自定义的注解.class文件内容。

这里Annotation是接口:

public interface Annotation {    }

那么证明,我们自定义的注解是extend接口Annotation,由此可知注解就是接口。

自定义注解注意点

解参数的可支持数据类型:

  • 所有基本数据类型(int,float,boolean,byte,double,char,long,short)

  • String类型

  • Class类型

  • enum类型

  • Annotation类型

  • 以上所有类型的数组

Annotation类型里面的参数该怎么设定:

  • 只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型。

  • 参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型 和 String,Enum,Class,annotations等数据类型,以及这一些类型的数组.例如,String value();这里的参数成员就为String。

  • 如果只有一个参数成员,最好把参数名称设为"value",后加小括号.例:下面的例子FruitName注解就只有一个参数成员。

如何获取注解?

共有以下五种方式:

  1. 获取类上的注解:Class类的getAnnotation()

  2. 获取方法上的注解:Method类的getAnnotation()

  3. 获取字段上的注解:Field类的 getAnnotation()

  4. 获取构造方法上的注解:Constructor类的getAnnotation()

  5. 获取包上的注解:Package类的getAnnotation()

如果此元素上存在指定的注释类型,则此方法返回该元素的注释,否则返回null。从上面的的集中方式中发现,都是使用getAnnotation()方法获取的,相信大多数人都能猜到为什么都是同一个方法名称。

下面就来说说Java中注解获取的鼻祖:

java.lang.reflect.AnnotatedElement

此接口所有方法

看看AnnotatedElement类图:

发现前面说的获取注解的类,全部都实现了AnnotatedElement接口。

所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的方法。

如下四个个方法来访问Annotation信息:

「getAnnotation」

返回该程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。

「getAnnotations」

返回该程序元素上存在的所有注解。

「isAnnotationPresent」

判断该程序元素上是否包指定类型的注解,存在则返回true,否则返回false。

「getDeclaredAnnotations」

返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响

感谢各位的阅读,以上就是"如何理解注解"的内容了,经过本文的学习后,相信大家对如何理解注解这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

注解 方法 类型 接口 参数 编译 程序 元素 成员 变量 时候 就是 数据 源码 数组 标记 注释 编译器 运行 名称 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 桐乡市梧桐界频网络技术服务部 深圳市仟讯网络技术公司 武汉软件开发工程师工资待遇 服务器可以用家用主板吗 互联先锋香港云服务器ip 汕尾软件开发难吗 军营网络安全宣传短片 深圳企业软件开发流程 成都厘源互联网科技有限公司 一个数据库包含哪几个文件 网络技术产品 礼品的开发 新媒体技术网络技术 并发式服务器 英语口语网络安全话题 小主机做网站服务器 高中网络技术基础知识教案 如何查找登录过的游戏服务器 苏州工业园区租房软件开发 普通服务器和pvp的区别 成都独角兽互联网科技公司地址 公考中关于网络安全的面试题 部队学习网络安全教育观后感 耐特康赛网络技术李明 山东口碑好的浪潮服务器公司 通达无法连接数据库 网站的数据库迁移 服务器串口 网络安全约谈信息简报 嘉兴运营网络技术包括什么 高青陶瓷软件开发定制
0