作者 :孙玉昌,昵称【一一哥 】,另外【壹壹哥】也是我哦
千锋教育高级教研员、CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者
前言
在上一篇文章中,壹哥 给大家介绍了"枚举"的内容,相信大家都已经掌握了这个简单知识的用法。今天我会给大家再介绍另一个简单的小知识,这就是"注解"!可能有些小伙伴听到这个名字会一愣,"注解"还要学习吗?我们之前在刚开始学Java时不是已经学习过了吗?壹哥请各位再仔细看一下,今天我们要学习的是"注解",不是"注释"哦!那么"注解"有什么特点呢?让我们一起来看看吧。
------------------------------前戏已做完,精彩即开始----------------------------
全文大约【4300】 字,不说废话,只讲可以让你学到技术、明白原理的纯干货!本文带有丰富的案例及配图视频,让你更好地理解和运用文中的技术概念,并可以给你带来具有足够启迪的思考......
配套开源项目资料
Github: github.com/SunLtd/Lear...
Gitee: gitee.com/sunyiyi/Lea...
一. 注解简介
1. 概念
注解是Java 5中推出的一种修饰符。从本质上来说,注解是一种元数据,可以为程序的各种元素(如类、方法、变量和参数)提供额外的补充信息。这些信息可以在编译、部署和运行时使用,但不会直接影响程序的执行。
这里壹哥给大家提到了"元数据"的概念,所谓的"元数据",是指可以提供与程序相关信息的数据。其中,"元"是"开头、第一、起始、最初、本源"的意思。所以,我们可以把"元数据"简单地理解为"初始的数据"。
从功能上来看,注解可以帮助我们实现很多效果,比如在项目编译时进行辅助检查、指示编译器生成代码,或者指示各种框架生成代码等。
另外,注解还和后面要学习的反射有着紧密的关系。我们可以利用反射来获取注解上携带的元数据等信息,比如可以使用反射来得到某个方法上的注解值,进而对这个方法进行一些高级的操作。关于反射,壹哥会在下一篇文章中给大家讲解,欢迎期待哦。
2. 注解与注释的区别
有些初学者因为知识掌握的不扎实,会把"注解"和"注释"搞混,其实这是两个完全不同的技术,差别很大。注解和注释虽然都是在代码中提供信息的方式,但它们的目的和使用方式有很大不同,其区别主要有以下几点:
- 注释是对相关代码进行解释说明,而注解是给有关代码提供额外的信息;
- 注释是帮助开发人员理解代码的,它们不会影响程序的执行。而注解常用于在代码中提供元数据,它们可以在编译、部署和运行时使用,也不会直接影响程序的执行;
- 注释可以用于任何的程序元素,而注解通常用于类、方法、变量和参数等声明上。
3. 使用场景
那么注解到底可以在哪些地方使用呢?比较常见的场景包括但不限于以下几个:
- 编译时检查:编译器可以检查某个注解是否存在,以及它们是否适用于特定的上下文。
- 指示编译器生成代码:注解可以指示编译器生成额外的代码。例如,使用注解可以指示编译器是否生成hashCode和equals方法。
- 指示框架生成代码:注解可以指示框架生成额外的代码。例如,使用注解可以指示Spring框架生成各种bean。
- 运行时处理:注解可以在运行时进行代码处理,例如使用反射来查找注解以实现某些特定的功能。
尤其是在各种框架中,更是大量使用了注解,比如Spring、Hibernate、JUnit、Jersey、Spring Boot和Spring Cloud等。
其中,Spring框架使用了大量的注解来实现依赖注入、AOP等功能;Hibernate框架使用了大量注解来实现数据访问;JUnit框架使用注解来标记测试方法和测试类;Jersey框架会使用注解来标记资源和方法。
而在Spring Boot和Spring Cloud中,也给我们提供了大量的注解来简化开发,并提供更多的功能。比如Spring Boot中有 @SpringBootApplication、@RestController, Spring Cloud中有 @EnableDiscoveryClient、@EnableCircuitBreaker、@FeignClient等 注解 。
总之,这些注解简化了程序的开发,并给我们提供了许多工具和功能来构建出高质量的代码,大大提高了我们的开发效率。
4. 注解类型
在Java中存在大量的注解,整体上可以分为以下三种类型:
- 元注解 :用于注解其他的注解,即"注解的注解"。Java 5中给我们定义了四个元注解: @Retention、@Target、@Documented和@Inherited。
- 内置注解:Java 5中有五个内置注解,后来在Java 8中又引入了两个,这7个内置注解包括:@Override、@Deprecated、@SuppressWarnings、@FunctionalInterface、@SafeVarargs、@Repeatable、@Native。
- 自定义注解:如果你觉得以上这些注解不够用,我们就可以自定义注解了,用于特定的用途。实际上各种开源框架中的注解,都属于自定义注解。
二. 基本使用
1. 基本语法
注解的使用很简单,我们在使用时以 "@" 符号开头 ,后面跟着注解的名称即可。当然每个注解都有自己的作用范围,不能到处乱用。另外我们在使用注解时,要注意注解中可以带有元素,这些元素可以在声明注解时设置初始值。使用注解的基本语法如下:
java
@AnnotationName(elementName = elementValue)
public void methodName() {
// method body
}
为了让大家更好地理解注解的用法,接下来壹哥就通过几个案例,来给大家讲解内置注解的用法。
2. 基本案例
2.1 内置注解含义
我们知道,Java 5中给我们引入了五个内置注解,后来在Java 8中又引入了两个,这7个内置注解的作用如下:
- @Override:用于指明某个方法重写了父类中的方法。
- @Deprecated:用于指明某个方法或类已过时,不再建议使用。
- @SuppressWarnings:用于抑制编译器警告,可以在类、方法、变量和参数等声明上使用。
- @FunctionalInterface:用于指明某个接口是函数式接口。函数式接口是指只包含一个抽象方法的接口。
- @SafeVarargs:用于指明某个方法使用了安全变长参数。它可以应用在静态方法、实例方法、构造函数和类上。
- @Repeatable:用于指明某个注解可以在同一个代码元素上重复使用,它可以用在注解上。
- @Native:用于指明某个方法的实现是本地方法。本地方法是指用其他语言(比如C、C++)编写的方法,它们可以使用Java Native Interface(JNI)与Java代码进行通信。
2.2 使用方式
第一个案例是关于@Override注解的用法,代码如下:
java
//父类
public class Parent {
//父类中的同名方法
public void print() {
System.out.println("Parent");
}
}
//子类
public class Child extends Parent {
//子类中的同名方法,重写了父类的同名方法
@Override
public void print() {
System.out.println("Child");
}
}
在这个例子中,我们在Child类的print()方法上使用了@Override注解,该注解就表明了子类的print()方法是覆盖了父类的同名方法。如果我们不小心写错了print()的方法名,编译器就会报错,这是因为我们使用了@Override注解来表明该方法是被覆盖的。所以,@Override注解主要是帮助我们避免因为错误的方法覆盖而引起的问题。
接下来第二个案例是使用@Deprecated注解的例子,代码如下:
java
@Deprecated
public void oldMethod() {
// method body
}
在这个例子中,oldMethod()方法被@Deprecated注解标记为过时的。如果我们在代码中调用了这个方法,编译器就会发出过时的警告,方法上会有黑色横线提升。
最后我们再来看看@SuppressWarnings注解的例子,代码如下:
java
@SuppressWarnings("unchecked")
public List<String> getList() {
return new ArrayList();
}
在这个例子中,getList()方法会被标记为"抑制未经检查的警告"。如果我们没有使用@SuppressWarnings注解,编译器会发出黄色叹号的警告,因为这里的getList()方法返回的是一个未经检查的List类型。
其他的内置注解用法与这些大同小异,只是各自都有不同的使用时机。总之,这些内置注解可以帮助我们编写出更加清晰、易于维护的代码,并提高代码的可读性和可靠性,请大家牢牢记住这几个内置注解即可。
如果你觉得这些内置注解不够用,那我们也可以自定义注解,以实现更多功能。
三. 自定义注解
1. 基本语法
如果我们想根据项目的特殊需要进行自定义注解,此时可以使用@interface关键字进行声明,并且要指定元注解、注解的类型和注解的生命周期。一般情况下,自定义注解的语法如下所示:
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
String value();
}
在这个示例中,壹哥 给大家自定义了一个新的注解@CustomAnnotation。在自定义这个注解时,壹哥用到了另外的4个元注解,分别是@Target和@Retention。那么这2个元注解具体有哪些含义呢?我们往下看。
2. 核心元注解
2.1 @Retention
@Retention是Java的一个元注解,用于指定注解的保留策略,或者说是注解的生命周期。Java 5中给我们定义了三种保留策略:
- RetentionPolicy.SOURCE:表示该注解仅保留在源代码中,编译器会在编译阶段忽略掉它们,生命周期最短。
- RetentionPolicy.CLASS:表示该注解会被编译器保留在类文件中,但在运行时不可用,生命周期居中。
- RetentionPolicy.RUNTIME:表示该注解会被编译器保留在类文件中,并在运行时也可以使用,生命周期最长。
我们这里所谓的"保留策略",也可以说成是"生命周期",也就是某个注解可以存活生效的阶段。比如,如果我们想在运行时也能使用自定义的注解,就可以使用以下声明:
java
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
// annotation elements
}
在这个例子中,@MyAnnotation这个自定义的注解被指定为RetentionPolicy.RUNTIME保留策略,所以它在运行时也可以使用。
另外,RetentionPolicy是一个枚举类,里面带了SOURCE、CLASS和RUNTIME这3个值,如下图所示:

2.2 @Target
@Target也是一个元注解,主要用于指定注解的作用范围,比如类、方法、变量和参数等。Java 5中给我们定义了以下几种作用范围:
- ElementType.ANNOTATION_TYPE:表示该注解适用在注解类型上。
- ElementType.CONSTRUCTOR:表示该注解适用于构造函数上。
- ElementType.FIELD:表示该注解适用于域或属性上。
- ElementType.LOCAL_VARIABLE:表示该注解适用于局部变量。
- ElementType.METHOD:表示该注解适用于方法。
- ElementType.PACKAGE:表示该注解适用于包。
- ElementType.PARAMETER:表示该注解适用于方法参数。
- ElementType.TYPE:表示该注解适用于类、接口和枚举类型。
其中ElementType也是一个枚举类,主要是配合@Target注解。

使用@Target元注解并配合ElementType,我们就可以限制某个注解的适用范围,从而确保注解的正确使用。
了解完这些理论内容之后,接下来我们再来看看具体的开发步骤吧。
3. 实现步骤
要想自定义一个注解,我们可以遵循以下实现步骤:
- 首先使用@interface关键字声明一个注解;
- 在该注解上指定元注解(@Retention、@Target);
- 然后指定注解的类型(如ElementType.METHOD);
- 接着自定义元素,表明该注解的基本属性;
- 最后在需要使用注解的地方,正确使用自定义的注解。
接下来壹哥就按照以上实现步骤,来给大家演示具体的自定义注解实现过程。
4. 实现案例
在以下代码中,壹哥定义了一个自定义注解@CustomAnnotation,该注解可以应用于方法,并具有一个String类型的元素value,代码如下:
java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author 一一哥Sun
* @company 千锋教育
*/
@Target(ElementType.METHOD)//限定作用范围
@Retention(RetentionPolicy.RUNTIME)//限定生命周期
public @interface CustomAnnotation {
//自定义元素
String value();
}
我们把这个注解可以在需要的方法上使用,如下所示:
java
/**
* @author 一一哥Sun
* @company 千锋教育
*/
public class Demo01 {
//@CustomAnnotation
//private String name;//不能在方法之外的其他地方使用这个自定义的注解,否则会报错
//使用自定义的注解
@CustomAnnotation(value = "一一哥")
public void myMethod() {
// method body
}
}
在这个例子中,myMethod()方法被可以@CustomAnnotation注解修饰,并且在该注解中带有一个元素value值"一一哥"。以后我们可以通过反射等技术,获取该注解的元素值,进而就可以进行其他的操作了。
5. 注意事项
注解的使用虽然简单,但我们在使用注解时,也需要注意以下几点:
- 注解主要是用于提供元数据,而不要直接影响程序的运行。因此,我们不能滥用注解,以免影响程序的可读性和维护性。
- 注解的定义应该清晰、简洁、易于理解,在注解的文档中要提供有关该注解的详细信息。
- 不同的注解要对应不同的保留策略和作用范围,应该根据需要选择合适的注解。
- 注解可以用于编译时检查、指示编译器生成代码、指示框架生成代码等,但注意不要过度依赖注解,以免影响程序的性能。
------------------------------正片已结束,来根事后烟----------------------------
四. 结语
通过今天的文章,壹哥就给大家介绍了Java里的注解。现在我们知道,注解是Java 5中推出的一项新功能,它们是一种元数据,可以为程序元素提供额外的信息,但并不会直接影响程序的执行。Java的注解有三种类型:元注解、内置注解和自定义注解。在开发时,我们可以根据需要自定义注解,并使用它们来提供有关代码的额外信息。
总之,注解是一种非常有用的元数据,可以给我们提供有关代码的额外信息,但要合理使用,以免影响程序的可读性和维护性,过度依赖注解也可能会导致程序的性能下降。
另外如果你独自学习觉得有很多困难,可以加入壹哥的学习互助群,大家一起交流学习。