Java基础 注解

ops/2025/1/11 12:37:04/

分类

  • Java自带的标准注解,包括@Override@Deprecated@SuppressWarnings,分别用于标明重写某个方法、标明某个类或方法过时、标明要忽略的警告,用这些注解标明后编译器就会进行检查。
  • 元注解,元注解是用于定义注解的注解,包括@Retention@Target@Inherited@Documented@Retention用于标明注解被保留的阶段,@Target用于标明注解使用的范围,@Inherited用于标明注解可继承,@Documented用于标明是否生成javadoc文档。
  • 自定义注解,可以根据自己的需求定义注解,并可用元注解对自定义注解进行注解。

作用

注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解。它主要的作用有以下四方面:

  • 生成文档,通过代码里标识的元数据生成javadoc文档。
  • 编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证。
  • 编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码。
  • 运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例。

Java内置注解

  • @Override:表示当前的方法定义将覆盖父类中的方法
  • @Deprecated:表示代码被弃用,如果使用了被@Deprecated注解的代码则编译器将发出警告
  • @SuppressWarnings:表示关闭编译器警告信息

@Override

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

@Deprecated

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
public @interface Deprecated {/*** Returns the version in which the annotated element became deprecated.* The version string is in the same format and namespace as the value of* the {@code @since} javadoc tag. The default value is the empty* string.** @return the version string* @since 9*/String since() default "";/*** Indicates whether the annotated element is subject to removal in a* future version. The default value is {@code false}.* 下个版本是否要删掉 注意删除和弃用的区别* @return whether the element is subject to removal* @since 9*/boolean forRemoval() default false;
}

@SuppressWarnings

@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {/*** The set of warnings that are to be suppressed by the compiler in the* annotated element.  Duplicate names are permitted.  The second and* successive occurrences of a name are ignored.  The presence of* unrecognized warning names is <i>not</i> an error: Compilers must* ignore any warning names they do not recognize.  They are, however,* free to emit a warning if an annotation contains an unrecognized* warning name.* @return the set of warnings to be suppressed*/String[] value();
}

可用于抑制各种类型的编译器警告,以下是常见参数:

参数

作用

"rawtypes"

抑制原始类型的警告

"unchecked"

抑制未检查的类型转换警告

"deprecation"

抑制使用过时方法或类的警告

"unused"

抑制未使用变量或方法的警告

"cast"

抑制不安全类型转换的警告

"all"

抑制所有警告

原始类型(Raw Types):指的是在使用泛型时没有指定具体类型。例如,List 被当作原始类型使用时,它没有指定类型参数,所以编译器会发出警告。

java
List list = new ArrayList(); // 使用原始类型,编译器会发出警告

示例

@SuppressWarnings("rawtypes")
public void someMethod() {List list = new ArrayList();  // 使用原始类型list.add("Hello");list.add(123);
}

元注解

注解类型进行注解的注解类,在JDK 1.5中提供了4个标准的元注解:@Target@Retention@Documented@Inherited, 在JDK 1.8中提供了两个元注解 @Repeatable@Native

@Target

Target注解的作用是:描述注解的使用范围(即:被修饰的注解可以用在什么地方) 。

Target注解用来说明那些被它所注解的注解类可修饰的对象范围:注解可以用于修饰 packages、types(类、接口、枚举、注解类)、类成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数),在定义注解类时使用了@Target 能够更加清晰的知道它能够被用来修饰哪些对象,它的取值范围定义在ElementType 枚举中。

public enum ElementType {TYPE, // 类、接口、枚举类FIELD, // 成员变量(包括:枚举常量)METHOD, // 成员方法PARAMETER, // 方法参数CONSTRUCTOR, // 构造方法LOCAL_VARIABLE, // 局部变量ANNOTATION_TYPE, // 注解类PACKAGE, // 可用于修饰:包TYPE_PARAMETER, // 类型参数,JDK 1.8 新增TYPE_USE // 使用类型的任何地方,JDK 1.8 新增}

@Retention

Reteniton注解的作用是:描述注解保留的时间范围(即:被描述的注解在它所修饰的类中可以被保留到何时) 。

public enum RetentionPolicy {SOURCE,    // 源文件保留CLASS,       // 编译期保留,默认值RUNTIME   // 运行期保留,可通过反射去获取注解信息
}

@Retention(RetentionPolicy.SOURCE)
public @interface SourcePolicy {}
@Retention(RetentionPolicy.CLASS)
public @interface ClassPolicy {}
@Retention(RetentionPolicy.RUNTIME)
public @interface RuntimePolicy {}

区别会在字节码上体现

  • 编译器并没有记录下 sourcePolicy() 方法的注解信息;
  • 编译器分别使用了 RuntimeInvisibleAnnotationsRuntimeVisibleAnnotations 属性去记录了classPolicy()方法 和 runtimePolicy()方法 的注解信息;

通过执行 javap -verbose RetentionTest命令获取到的RetentionTest 的 class 字节码内容如下。

{public retention.RetentionTest();flags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 3: 0public void sourcePolicy();flags: ACC_PUBLICCode:stack=0, locals=1, args_size=10: returnLineNumberTable:line 7: 0public void classPolicy();flags: ACC_PUBLICCode:stack=0, locals=1, args_size=10: returnLineNumberTable:line 11: 0RuntimeInvisibleAnnotations:0: #11()public void runtimePolicy();flags: ACC_PUBLICCode:stack=0, locals=1, args_size=10: returnLineNumberTable:line 15: 0RuntimeVisibleAnnotations:0: #14()
}

@Documented

Documented注解的作用是:描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息。

@Inherited

Inherited注解的作用:被它修饰的Annotation将具有继承性。如果某个类使用了被@Inherited修饰的Annotation,则其子类将自动具有该注解。

/*** 父类* @author crazy*/
@InheritedTest("使用Inherited的注解 class")
@InheritedTest2("未使用Inherited的注解 class")
public class Parent {@InheritedTest("使用Inherited的注解 method")@InheritedTest2("未使用Inherited的注解 method")public void method(){}@InheritedTest("使用Inherited的注解 method2")@InheritedTest2("未使用Inherited的注解 method2")public void method2(){}@InheritedTest("使用Inherited的注解 field")@InheritedTest2("未使用Inherited的注解 field")public String a;
}
/*** 子类  只继承了一个method方法* @author crazy*/
public class Child extends Parent {@Overridepublic void method() {}
}
/*** 通过反射进行测试* @author crazy*/
public class test {public static void main(String[] args) throws NoSuchMethodException, SecurityException, NoSuchFieldException {Class<Child> clazz = Child.class;//对类进行测试 System.out.println("对类进行测试");if(clazz.isAnnotationPresent(InheritedTest.class)){System.out.println(clazz.getAnnotation(InheritedTest.class).value());}if(clazz.isAnnotationPresent(InheritedTest2.class)){System.out.println(clazz.getAnnotation(InheritedTest2.class).value());}System.out.println();//对方法 进行测试System.out.println("对方法进行测试");Method method = clazz.getMethod("method", null);if(method.isAnnotationPresent(InheritedTest.class)){System.out.println(method.getAnnotation(InheritedTest.class).value());}if(method.isAnnotationPresent(InheritedTest2.class)){System.out.println(method.getAnnotation(InheritedTest2.class).value());}System.out.println();//对方法2 进行测试System.out.println("对方法2进行测试");Method method2 = clazz.getMethod("method2", null);if(method2.isAnnotationPresent(InheritedTest.class)){System.out.println(method2.getAnnotation(InheritedTest.class).value());}if(method2.isAnnotationPresent(InheritedTest2.class)){System.out.println(method2.getAnnotation(InheritedTest2.class).value());}System.out.println();//对属性测试System.out.println("对属性进行测试");Field field = clazz.getField("a");if(field.isAnnotationPresent(InheritedTest.class)){System.out.println(field.getAnnotation(InheritedTest.class).value());}if(field.isAnnotationPresent(InheritedTest2.class)){System.out.println(field.getAnnotation(InheritedTest2.class).value());}}
}
对类进行测试
使用Inherited的注解 class对方法进行测试对方法2进行测试
使用Inherited的注解 method2
未使用Inherited的注解 method2对属性进行测试
使用Inherited的注解 field
未使用Inherited的注解 field

由上可以看出,通过Inherited元注解声明的自定义注解,在类上使用时,可以被子类继承,对第一个方法进行测试时,由于子类继承了父类方法,且两个都没有输出,证明Inherited对方法无效,由方法2可以看出,因为子类没有重写父类方法,所以是直接使用的父类方法,所以两个都会输出,同理属性也是,都会输出。

所以证明最后结论:通过对注解上使用元注解Inherited声明出的注解,在使用时用在类上,可以被子类所继承,对属性或方法无效。

元注解 - @Repeatable (Java8)

@Repeatable请参考Java 8 - 重复注解

元注解 - @Native (Java8)

使用 @Native 注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。对于 @Native 注解不常使用,了解即可

注解与反射

定义注解后,如何获取注解中的内容呢?反射包java.lang.reflect下的AnnotatedElement接口提供这些方法。这里注意:只有注解被定义为RUNTIME后,该注解才能是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。

AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的方法来访问Annotation信息。我们看下具体的先关接口

  • boolean isAnnotationPresent(Class<?extends Annotation> annotationClass)

判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false。注意:此方法会忽略注解对应的注解容器。

  • <T extends Annotation> T getAnnotation(Class<T> annotationClass)

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

  • Annotation[] getAnnotations()

返回该程序元素上存在的所有注解,若没有注解,返回长度为0的数组。

  • <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass)

返回该程序元素上存在的、指定类型的注解数组。没有注解对应类型的注解时,返回长度为0的数组。该方法的调用者可以随意修改返回的数组,而不会对其他调用者返回的数组产生任何影响。getAnnotationsByType方法与 getAnnotation的区别在于,getAnnotationsByType会检测注解对应的重复注解容器。若程序元素为类,当前类上找不到注解,且该注解为可继承的,则会去父类上检测对应的注解。

  • <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass)

返回直接存在于此元素上的所有注解。与此接口中的其他方法不同,该方法将忽略继承的注释。如果没有注释直接存在于此元素上,则返回null

  • <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass)

返回直接存在于此元素上的所有注解。与此接口中的其他方法不同,该方法将忽略继承的注释

  • Annotation[] getDeclaredAnnotations()

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

注解的应用场景

框架的演进

Spring 框架 配置化到注解化的转变。

继承实现到注解实现 - Junit3到Junit4
 

自定义注解和AOP - 通过切面实现解耦


 


http://www.ppmy.cn/ops/149160.html

相关文章

关于Mac中的shell

1 MacOS中的shell 介绍&#xff1a; 在 macOS 系统中&#xff0c;Shell 是命令行与系统交互的工具&#xff0c;用于执行命令、运行脚本和管理系统。macOS 提供了多种 Shell&#xff0c;主要包括 bash 和 zsh。在 macOS Catalina&#xff08;10.15&#xff09;之前&#xff0c…

zookeeper监听机制(Watcher机制)

文章目录 引言I zookeeper监听机制Watcher机制实现分布式的通知功能触发事件种类Watcher的三个过程II watch机制特点一次性触发事件封装event异步发送先注册再触发常见的通知状态和事件类型III 应用案例(Kafka)Kafka的消息模型Kafka在Zookeeper中保存的元数据Kafka 基于Contr…

使用R包Corrplot绘制相关性图

前记 Corrplot是一个用于可视化相关性矩阵的R包。它提供了各种方法和选项来创建高质量的相关性图&#xff0c;并帮助用户更好地理解数据集中的变量之间的相关性。 相关性是指两个或多个变量之间的统计关系&#xff0c;它衡量了这些变量之间的线性关系的强度和方向。在数据分析和…

HTML-表单标签

一、form表单容器标签 <form> 标签在 HTML 中用于定义一个表单容器&#xff0c;用于收集用户输入的数据。它是所有表单元素&#xff08;如文本框、单选按钮、复选框、下拉菜单等&#xff09;的父元素。以下是关于 <form> 标签的详细介绍&#xff0c;包括其特点、用…

算法6--模拟

目录 基础经典例题1576. [替换所有的问号](https://leetcode.cn/problems/replace-all-s-to-avoid-consecutive-repeating-characters/description/)495.[ 提莫攻击](https://leetcode.cn/problems/teemo-attacking/submissions/460223504/)[6. Z 字形变换](https://leetcode.c…

python无需验证码免登录12306抢票 --selenium(2)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 [TOC](python无需验证码免登录12306抢票 --selenium(2)) 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 就在刚刚我抢的票&#xff1a;2025年1月8日…

数据采集标注在智能导航系统中的应用案例

‌智能导航系统是一种基于GPS定位技术和人工智能算法的导航软件&#xff0c;其可根据用户的位置、路线和交通情况等信息&#xff0c;提供最佳的出行路线和导航服务‌。‌智能导航系统综合应用了信息管理、认知心理学与行为学、人工智能等多学科理论与技术&#xff0c;自主识别用…

深入Android架构(从线程到AIDL)_22 IPC的Proxy-Stub设计模式04

目录 5、 谁来写Proxy及Stub类呢? 如何考虑人的分工 IA接口知识取得的难题 在编程上&#xff0c;有什么技术可以实现这个方法&#xff1f; 范例 5、 谁来写Proxy及Stub类呢? -- 强龙提供AIDL工具&#xff0c;给地头蛇产出Proxy和Stub类 如何考虑人的分工 由框架开发者…