java注解全网最细

引言

在java编程中,注解(Annotation)是一种元数据,它提供了关于程序代码的额外信息。注解不直接影响程序的执行,但可以在运行时提供有关程序的信息,或者让编译器执行额外的检查。

下面笔者通过循序渐进的方式一步步介绍注解的相关内容,帮助大家消化吸收知识点。

一、何谓java注解

Java注解又称Java标注,是在 JDK5 时引入的新特性,注解(也被称为元数据)。
Java注解它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。

Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。

二、java注解分类

1. 内置注解(Built-in Annotations):

这些注解是Java标准库(java.lang包或相关包)中预先定义的,用于特定的编程目的。例如:

  • @Override:表示方法重写父类的方法。
  • @Deprecated:标记过时的方法或类,编译器会发出警告。
  • @SuppressWarnings:抑制特定的编译器警告。
  • @FunctionalInterface:标识一个函数式接口,即只有一个抽象方法的接口。
  • @SafeVarargs:表示方法的安全可变参数列表,避免泛型警告。
@Override示例
/*** override注解* @author hulei* @date 2024/5/10 13:52*/public class OverrideAnnotations {static class BaseUser implements UserInterface {@Overridepublic void method() {new BaseUser().method();}}interface UserInterface {void method();}
}

这个注解没什么好说的,一般用在子类覆写父类的方法上,比较简单基础的注解。上图代码表示的是:BaseUser 类实现了 UserInterface 接口,并重写了method()方法,方法上标识了 @Override注解。不一定非要是实现一个接口,继承一个普通类或者抽象类,重写父类的方法也可。

在Java中,如果子类重写父类的方法但不使用@Override注解,会有以下几点需要注意:

  1. 编译器提示
    如果你没有使用@Override,但实际上是重写了父类方法,某些IDE(如Eclipse, IntelliJ IDEA)会在方法上显示警告,提示你可能遗漏了@Override注解。虽然这不是强制性的,但添加它有助于提高代码的可读性和清晰度。

  2. 编译错误
    如果方法签名(包括方法名、参数列表和返回类型)与父类方法不完全匹配,编译器不会报错,因为你实际上并没有重写方法。这可能导致意外的行为,因为你可能以为你在调用子类的方法,但实际上调用了父类的方法。

  3. 方法覆盖的确认
    使用@Override可以确保编译器在编译时检查你是否真正重写了父类的方法。如果签名不匹配,编译器会报错,防止因意外的非重写而导致的问题。

  4. 代码可读性
    添加@Override注解使代码更易读,因为它清楚地表明该方法是用于重写父类方法的。

  5. 未来修改的保护
    如果父类的签名在未来发生变化,而你没有更新子类的方法签名,没有@Override的子类方法将不再重写父类方法。而如果有@Override,编译器会报错,提醒你需要更新子类的方法。

因此,尽管不是必须的,但推荐在重写父类方法时使用@Override注解,以确保代码的正确性和一致性。

@Deprecated示例
package com.datastructures;/*** Deprecated注解* @author hulei* @date 2024/5/10 14:06*/public class DeprecatedAnnotation {public static void main(String[] args) {DeprecatedAnnotation deprecatedAnnotation = new DeprecatedAnnotation();deprecatedAnnotation.method();}@Deprecatedpublic void method() {System.out.println("DeprecatedAnnotation.method");}
}

在这里插入图片描述

调用一个过时的方法,大部分编译器比如IntelliJ IDEA会给出警告信息,不推荐使用。像我们在开发过程中使用很多的第三方库或者框架包括jdk自身的大量类库时,可能早期提供的方法或函数有缺陷,但是又被大量的开发者使用,所以不能删除,这些第三方库的作者就在过时的方法加上这个注解,api调用者在调用这个过时方法就会收到提示,从而查看源码,根据作者的注释指引调用新的更加安全的方法。

@SuppressWarnings示例
package com.datastructures;
import java.util.ArrayList;
import java.util.List;/*** @author hulei* @date 2024/5/10 14:31*/public class SuppressWarningsAnnotation {@SuppressWarnings("all")public static void addItems(String item){List items = new ArrayList();items.add(item);}public static void main(String[] args) {addItems("item");}
}

如果不加@SuppressWarnings注解,则会出现如下提示
在这里插入图片描述
看着很不舒服,都是一些无关紧要的提示,比如类型检查操作的警告,装箱、拆箱操作时候的警告等等。
加了 @SuppressWarnings(“all”) 这个注解,告警信息就没有了,抑制类所有类型的告警信息,清清爽爽,这对强迫症患者极为友好。

在这里插入图片描述
就算是加了过时注解的方法,加了@SuppressWarnings(“all”),也会把过时告警信息隐蔽掉。
在这里插入图片描述

我们一般常用的是如下三种

  1. @SuppressWarnings(“unchecked”) :抑制单类型的警告
  2. @SuppressWarnings(value={“unchecked”, “rawtypes”}) :抑制多类型的警告
  3. @SuppressWarnings(“all”) :抑制所有类型的警告

抑制警告的关键字对照表

关键字用途描述
allto suppress all warnings抑制所有警告
boxingto suppress warnings relative to boxing/unboxing operations抑制装箱、拆箱操作时候的警告
castto suppress warnings relative to cast operations抑制映射相关的警告
dep-annto suppress warnings relative to deprecated annotation抑制启用注释的警告
deprecationto suppress warnings relative to deprecation抑制过期方法警告
fallthroughto suppress warnings relative to missing breaks in switch statements抑制确在switch中缺失breaks的警告
finallyto suppress warnings relative to finally block that don’t return抑制finally模块没有返回的警告
hidingto suppress warnings relative to locals that hide variable抑制相对于隐藏变量的局部的警告
incomplete-switchto suppress warnings relative to missing entries in a switch statement (enum case)忽略没有完整的switch语句
nlsto suppress warnings relative to non-nls string literals忽略非nls格式的字符
nullto suppress warnings relative to null analysis忽略对null的操作
rawtypesto suppress warnings relative to un-specific types when using generics on class params使用generics时忽略没有指定相应的类型
restrictionto suppress warnings relative to usage of discouraged or forbidden references抑制禁止引用的使用相关的警告
serialto suppress warnings relative to missing serialVersionUID field for a serializable class忽略在serializable类中没有声明serialVersionUID变量
static-accessto suppress warnings relative to incorrect static access抑制不正确的静态访问方式警告
synthetic-accessto suppress warnings relative to unoptimized access from inner classes抑制子类没有按最优方法访问内部类的警告
uncheckedto suppress warnings relative to unchecked operations抑制没有进行类型检查操作的警告
unqualified-field-accessto suppress warnings relative to field access unqualified抑制没有权限访问的域的警告
unusedto suppress warnings relative to unused code抑制没被使用过的代码的警告
@FunctionalInterface示例
@FunctionalInterface
public interface Function<T, R> {R apply(T t);default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {Objects.requireNonNull(before);return (V v) -> apply(before.apply(v));}default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {Objects.requireNonNull(after);return (T t) -> after.apply(apply(t));}static <T> Function<T, T> identity() {return t -> t;}
}

这里就拿JDK官方的Function函数式接口为例,注释被我删除了
打上@FunctionalInterface注解的接口,就可以使用java8提供的lamda表达式来表示该接口的一个实现(注:JAVA 8 之前一般是用匿名类实现的)。
如下代码调用实例
在这里插入图片描述
表示给定一个入参,经过一定的逻辑处理,返回一个出参结果
还有BiFunction,给定两个入参,返回一个出参结果,
也可以自定义,多个入参,比如笔者自定义的 ThreeBiFunction就是三个入参,一个出参
在这里插入图片描述

@SafeVarargs示例
package com.datastructures;import java.util.List;
import java.util.Optional;/*** 注解:SafeVarargs示例*/
public class SafeVarargsAnnotations {@SafeVarargsstatic void function(List<String>... stringLists) {}abstract static class BaseUser implements UserInterface {@SafeVarargsfinal <T> void gamma(T... ts) {}@Override@SafeVarargspublic final void method(Optional<Object>... optionals) {UserInterface.super.method(optionals);}}interface UserInterface {default void method(Optional<Object>... optionals) {}@SafeVarargsstatic <T> void gamma(Class<T>... classes) {}void method();}}

方法的参数包含可变参数列表时,不加这个@SafeVarargs注解就会有告警信息,比如上面的代码,method方法有可变参数列表,没有加注解,产生类型安全和泛型相关提示
在这里插入图片描述

2.元注解(Meta-Annotations):

元注解是用于注解其他注解的注解,是所有其他注解的基础,它们定义了注解的行为和生命周期。主要包括:

  • @Retention:定义注解的保留策略,可以是SOURCE(只存在于源码中)、CLASS(编译时丢弃,存在于字节码中但不运行时可用)或RUNTIME(运行时可通过反射访问)。
  • @Target:指定注解可以应用于哪些程序元素,如类、方法、字段等。
  • @Documented:指示是否将注解包含在生成的Javadoc中。
  • @Inherited:允许子类继承父类的注解(仅适用于类,不适用于方法或字段)。
@Retention示例

在这里插入图片描述
上面的@SuppressWarnings注解源码,就只有一个 @Retention注解
打上@Retention注解的其他注解,有三个保留策略,上面已经说明。
在这里插入图片描述

@Target示例

如果一个注解上有@Target注解,则@Target注解声明了这个注解可以使用的地方
在这里插入图片描述
比如这个自定义注解,就只能在方法上使用,ElementType.METHOD枚举就是方法声明限制,关于ElementType枚举,可以自行查看里面的枚举信息
当然,后面可以写多个使用场景的枚举声明
在这里插入图片描述

在这里插入图片描述
还有的注解,没有加@Target注解,比如上面的@SuppressWarnings注解。一个注解上没有加使用范围的注解@Targe,那这个注解可以使用在任何能够使用注解的地方。所以 @SuppressWarnings 不包含自己的 @Target 注解,意味着它理论上可以应用于 Java 规范中任何允许注解的地方。然而,它实际上的使用受到限制,尤其是不能在表达式上下文中使用,这是因为其设计目的和 Java 语言规范的限制

  • 设计目的:@SuppressWarnings 的设计初衷是为了告诉编译器在特定的范围(如类、方法、字段等)内忽略特定类型的警告。它是为了简化开发过程,允许开发者在明知某些代码可能引起编译器警告,但确认这些警告不影响程序正确性的情况下,有选择地忽略这些警告。因此,它主要应用于编译单位的较大结构上。

  • 表达式上下文限制:表达式上下文通常涉及更细粒度的操作,如赋值、方法调用、算术运算等。在这些上下文中使用 @SuppressWarnings 不符合其设计逻辑,因为这些地方通常不涉及整体性的类型或结构警告,而是更具体的、即时的操作。如果允许在表达式中使用,不仅会增加语言的复杂性,还可能引发滥用,使得代码难以理解和维护。

  • 类型注解与普通注解的区别:类型注解(自 Java 8 引入)专门设计用于标注类型声明,包括泛型类型参数、返回类型、参数类型等,而 @SuppressWarnings 并不属于这一类别。类型注解可以在某种程度上改变编译器对类型的理解,而 @SuppressWarnings 仅用于指示编译器如何处理警告信息,不改变代码的类型系统或结构。

  • Java 语言规范限制:即使 @SuppressWarnings 没有限定其 @Target,Java 语言规范和编译器实现也决定了哪些注解可以用在哪些上下文中。表达式上下文通常不接受注解,特别是像 @SuppressWarnings 这样旨在影响编译器警告处理的注解,因为这不符合语言的语义和设计哲学。

举例如下:
在这里插入图片描述
在这里插入图片描述
综上所述,@SuppressWarnings 不能在表达式上下文中使用,主要是由于其设计意图、语言规范的限制以及为了保持语言的清晰度和简洁性。

@Documented示例

这个注解不重要,表示是否将注解包含在生成的Javadoc中。加不加完全在于我们自己,只要知道的用途就行了

@Inherited示例

这个注解还是比较重要的,允许子类继承父类的注解(仅适用于类,不适用于方法或字段)。什么意思呢?
一个注解上有@Inherited注解,那么当我们把这个注解打在一个类上时,如果这个类有子类,那么这个子类继承父类的这个注解

package com.datastructures;import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;/*** @author hulei* @date 2024/5/10 17:01*/public class InheritedAnnotation {/*** 自定义注解*/@Inherited@Retention(RetentionPolicy.RUNTIME)@interface InnerAnnotation{String value();}/*** 父类,使用了自定义注解*/@InnerAnnotation(value = "父类注解")public static class ParentClass {}/*** 子类继承父类*/public static class ChildClass extends ParentClass {}public static void main(String[] args) {Class<?> childClass = ChildClass.class;if (childClass.isAnnotationPresent(InnerAnnotation.class)) {InnerAnnotation annotation = childClass.getAnnotation(InnerAnnotation.class);System.out.println("Value from InnerAnnotation: " + annotation.value());} else {System.out.println("No InnerAnnotation found.");}}}

运行可以看到,子类也获取到了这个注解

在这里插入图片描述
那我们改造下,把注解上的@Inherited注解去掉,再执行看看
在这里插入图片描述
在这里插入图片描述
可以看到子类没有获取到父类的注解了,即没有从父类继承

3.自定义注解(Custom Annotations):

开发者可以使用**@interface**关键字创建自己的注解,根据需求定义注解的行为和用途。自定义注解可以结合元注解来定义其行为,例如通过@Retention和@Target来控制自定义注解的生命周期和应用范围。下面给出几个示例:

1. 用于记录日志的注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface LogExecution {String message() default "";
}

这个注解可以应用于方法,表示在执行该方法前/后需要记录日志。

切面类aop

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;@Aspect
@Component
public class LoggingAspect {private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);@Around("@annotation(LogExecution)")public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {String methodName = joinPoint.getSignature().getName();String className = joinPoint.getSignature().getDeclaringTypeName();String message = joinPoint.getSignature().getAnnotation(LogExecution.class).message();long start = System.currentTimeMillis();logger.info("Starting method: {}.{} with message: {}", className, methodName, message);Object result = joinPoint.proceed(); // 继续执行目标方法long elapsedTime = System.currentTimeMillis() - start;logger.info("Completed method: {}.{} in {}ms", className, methodName, elapsedTime);return result;}
}

实际代码调用

@Service
public class SomeService {@LogExecution(message = "Executing business logic")public String performTask() {// 示例业务逻辑try {Thread.sleep(100); // 模拟耗时操作} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new RuntimeException(e);}return "Task completed";}
}
2.用于数据验证的注解

比如邮箱格式验证

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;@Constraint(validatedBy = EmailValidator.class)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EmailVaild{String message() default "邮箱格式不正确";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}
3.用于事务管理的注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Transactional {boolean readOnly() default false;
}

这个注解用于标记一个方法需要在数据库事务中执行,readOnly 参数表示是否为只读事务。

4.用于权限控制的注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface RequiresRole {String[] roles() default {};
}

这个注解用于标记一个方法或类需要特定的角色才能访问,roles 参数是角色的数组。

5.用于缓存结果的注解
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CacheResult {long cacheTime() default 60; // 缓存60秒
}

这个注解用于标记一个方法的结果应该被缓存一定时间,cacheTime 参数表示缓存的秒数。

在实际使用中,这些注解通常会与AOP(面向切面编程)框架结合,如Spring AOP,以便在运行时动态地处理注解的逻辑。

三、注解中的属性省略问题

这一节是笔者在学习时遇到的疑问,这里作为记录
​​​​在这里插入图片描述
这个注解有两个属性,value和logical ,@HasPermissions(“system:user:query”)

这种写法会把属性值默认给value,注意必须要有名为value的属性,并且其他属性都有默认值才可以

否则得显示给属性赋值

@HasPermissions(value = {"om:deviceCascade:edit","om:deviceCascade:add"},logical = Logical.OR)

总结如下

  • 如果注解只有一个属性,那么肯定是赋值给该属性。

  • 如果注解有多个属性,而且前提是这多个属性都有默认值,那么你不写注解名赋值,会赋值给名字为“value”这属性。

  • 如果注解有多个属性,其中有没有设置默认值的属性,那么当你不写属性名进行赋值的时候,是会报错的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/685520.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【全开源】Java外卖霸王餐免费吃外卖小程序+APP+公众号+H5多端霸王餐源码

一、特色功能 霸王餐活动管理&#xff1a;允许商家发布和管理霸王餐活动&#xff0c;包括设置活动时间、具体优惠、活动规则等。用户参与与浏览&#xff1a;用户可以在小程序中浏览霸王餐活动列表&#xff0c;查看活动的详情信息&#xff0c;如商品或服务的免费赠送、活动规则…

用户体验优化uxo指的是什么?

用户体验优化(User Experience Optimization&#xff0c;简称UXO)是一种专注于改善和提升用户在使用企业产品或服务时的整体感受和体验的过程。简单来说&#xff0c;它旨在通过改进产品或服务的设计和功能&#xff0c;使用户在使用过程中感到更加愉悦、满意和高效。用户体验优化…

设计软件有哪些?渲染软件篇(3),渲染100邀请码1a12

今天我们继续介绍几款渲染软件&#xff0c;方便大家了解 1、渲染100(http://www.xuanran100.com/?ycode1a12) 渲染100是网渲平台&#xff0c;为设计师提供高性能的渲染服务。通过它设计师可以把本地渲染移到云端进行&#xff0c;速度快价格便宜&#xff0c;支持3dmax、vray、…

LaTeX公式学习笔记

\sqrt[3]{100} \frac{2}{3} \sum_{i0}^{n} x^{3} \log_{a}{b} \vec{a} \bar{a} \lim_{x \to \infty} \Delta A B C

ICode国际青少年编程竞赛- Python-3级训练场-坐标判断1

ICode国际青少年编程竞赛- Python-3级训练场-坐标判断1 1、 for i in range(4): Spaceship.step(3)Spaceship.turnLeft()Spaceship.step(3)Spaceship.turnRight()if Item[i].y < Dev.y:Dev.step(3)Dev.step(-3)2、 for i in range(9): if Flyer[i].y ! Dev.y:Flyer[i]…

【半夜学习MySQL】表结构的操作(含表的创建、修改、删除操作,及如何查看表结构)

&#x1f3e0;关于专栏&#xff1a;半夜学习MySQL专栏用于记录MySQL数据相关内容。 &#x1f3af;每天努力一点点&#xff0c;技术变化看得见 文章目录 创建表查看表结构修改表删除表 创建表 语法&#xff1a; create table table_name(field1 datatype,field2 datatype,fiel…

Python作业三:扫描目录文件,发送到指定邮箱

问&#xff1a; 作业任务&#xff1a;编写python代码&#xff0c;扫描指定的目录下的所有文件&#xff0c;将这些扫描的文本内容邮件发送到指定邮箱(如&#xff1a;自己的qq邮箱) 发送邮箱&#xff1a;yagmail 以 163 邮箱为例&#xff0c;在编码之前&#xff0c;我们需要开…

node pnpm修改默认包的存储路径

pnpm与npm的区别 PNPM和NPM是两个不同的包管理工具。 NPM&#xff08;Node Package Manager&#xff09;是Node.js的官方包管理工具&#xff0c;用于安装、发布和管理Node.js模块。NPM将包安装在项目的node_modules目录中&#xff0c;每个包都有自己的依赖树。 PNPM&#xf…

Qexo:让你的静态博客动起来

Qexo是一个强大而美观的在线静态博客编辑器&#xff0c;它不仅限于编辑&#xff0c;而是将静态博客提升到新的高度。通过GPL3.0开源协议&#xff0c;Qexo提供了一个集编辑、管理、扩展于一体的平台&#xff0c;让静态博客也能拥有动态的元素。无论你是Hexo、Hugo还是Valaxy的用…

教你解决PUBG绝地求生卡在初始界面 登不上去 打不开游戏的问题

在热门大逃杀游戏《绝地求生》&#xff08;PUBG&#xff09;里&#xff0c;紧张刺激的战斗和高度还原的战场环境深深吸引着全球玩家的心。然而&#xff0c;在经历一场紧张激烈的生存挑战后&#xff0c;部分玩家遭遇了一段不太愉快的小插曲&#xff1a;游戏在胜利或战败的结算界…

3. 多层感知机算法和异或门的 Python 实现

前面介绍过感知机算法和一些简单的 Python 实践&#xff0c;这些都是单层实现&#xff0c;感知机还可以通过叠加层来构建多层感知机。 2. 感知机算法和简单 Python 实现-CSDN博客 1. 多层感知机介绍 单层感知机只能表示线性空间&#xff0c;多层感知机就可以表示非线性空间。…

Java毕业设计 基于SpringBoot vue新能源充电系统

Java毕业设计 基于SpringBoot vue新能源充电系统 SpringBoot 新能源充电系统 功能介绍 首页 图片轮播 充电桩 充电桩类型 充电桩详情 充电桩预约 新能源公告 公告详情 登录注册 个人中心 余额充值 修改密码 充电桩报修 充电桩预约订单 客服 后台管理 登录 个人中心 修改密码…