《重学Java系列》之 注解基础
不诗意的女程序媛不是好厨师~
转载请注明出处,From李诗雨---[https://blog.csdn.net/cjm2484836553/article/details/104432728]
按照惯例我们先来看下本篇的思维导图:
在这里插入图片描述
本篇来和大家先学习一下注解基础,后期再一步一步深入学习。
本篇的主要目的就是先对注解有些基本的了解,后期会进行大量的代码实战,这样循序渐进,相信我们就可以很好的掌握注解了。
1.什么是注解?
【注解的定义】:
Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java5 开始添加到 Java 的。
【通俗点理解】:
注解即标签。
如果把代码想象成一个具有生命的个体,注解就是给这些代码的某些个体打标签。
2.自定义注解及使用
- 注解通过 @interface关键字进行定义。
public @interface Test {
}
它的形式跟接口很类似,不过前面多了一个 @ 符号。
上面的代码就创建了一个名字为 Test 的注解。你可以简单理解为创建了一张名字为 Test的标签。
- 使用注解
@Test
public class TestAnnotation {
}
创建一个类 TestAnnotation,然后在类定义的地方加上 @Test就可以用 Test注解这个类了。
你可以简单理解为将 Test 这张标签贴到 TestAnnotation这个类上面。
3.元注解
3.1元注解的理解
元注解:即注解的注解。
元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。
如果难于理解的话,你可以这样理解。元注解也是一张标签,但是它是一张特殊的标签,它的作用和目的就是给其他普通的标签进行解释说明的。
3.2 常见的5种元注解。
元注解有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 种。
下面分别一一来说明:
@Retention --- 指定了注解的存活时间
Retention 的英文意为保留期的意思。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间。
它的取值如下:
- RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。 ----生命周期最短的注解
- RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
- RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们
我画一张图来帮助大家理解 这三个取值下 注解 所能保留到的时期:
在这里插入图片描述@Target ---- 指定注解用在什么地方
Target 是目标的意思,@Target 指定了注解运用的地方
你可以这样理解,当一个注解被 @Target 注解时,这个注解就被限定了运用的场景。
类比到标签,原本标签是你想张贴到哪个地方就到哪个地方,但是因为 @Target 的存在,它张贴的地方就非常具体了,比如只能张贴到方法上、类上、方法参数上等等。@Target 有下面的取值
- ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
- ElementType.CONSTRUCTOR 可以给构造方法进行注解
- ElementType.FIELD 可以给属性进行注解
- ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
- ElementType.METHOD 可以给方法进行注解
- ElementType.PACKAGE 可以给一个包进行注解
- ElementType.PARAMETER 可以给一个方法内的参数进行注解
@Inherited --- 允许子类继承父类的注解。
Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。
这么说比较抽象,举个栗子给大家看看:
现在有一个 美女类Beauty,还有一个它的子类 BeautyChild。
那么当Beauty类被打上了“美女”的标签后,她的孩子BeautyChild默认也会拥有“美女标签”。
(PS:也就是美女的孩子还是美女。)
//1.自定义一个使用了@Inherited的标签
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface InheritedTest {
String value();
}
//2.美女类Beauty,并且拥有一个“美女”标签
@InheritedTest("美女")
public class Beauty {
}
//3.美女类的孩子类BeautyChild,并没有特地指定任何标签
class BeautyChild extends Beauty {
}
//4.测试:看从子类中能否取到标签
public class DemoTest {
public static void main(String[] args) {
Class<BeautyChild> beautyChildClass = BeautyChild.class;
if (beautyChildClass.isAnnotationPresent(InheritedTest.class)) {
System.out.println(beautyChildClass.getAnnotation(InheritedTest.class).value());
}
}
}
结果是:从子类中确实取到了父类所拥有的标签。
在这里插入图片描述
@Repeatable --- 注解的值可以同时取多个
Repeatable 是可重复的意思。@Repeatable 是 Java 1.8 才加进来的,所以算是一个新的特性。
什么样的注解会多次应用呢?通常是注解的值可以同时取多个。
还是来举个栗子来方便大家理解吧:
比如说我吧,在工作上,我拥有“程序媛”的标签;在朋友眼中,我拥有“疯一样的女子”的标签;在陌生人眼中,我又拥有“淑女”的标签。根据不同的场合,我可以拥有很多不同的标签,这完全没有问题。
//1.先声明一个Persons类用来包含所有的身份
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Persons {
Person[] value();//由于会有多个身份,所以此处是数组
}
//2.定义一个Person注解,它使用到了@Repeatable。
//@Repeatable括号内的就相当于用来保存该注解内容的容器。
@Repeatable(Persons.class)
public @interface Person {
String role() default "";
}
//3.声明一个Shiyu类,给该类加上一些身份。
@Person(role = "程序媛")
@Person(role = "疯一样的女子")
@Person(role = "淑女")
public class Shiyu {
}
//4.测试看看能否获取这多个标签
public class DemoTest2 {
public static void main(String[] args) {
if (Shiyu.class.isAnnotationPresent(Persons.class)) {
Persons p2 = Shiyu.class.getAnnotation(Persons.class);
for (Person t : p2.value()) {
System.out.println(t.role());
}
}
}
}
结果标签全部可以打印出来,则正确:
在这里插入图片描述
【注意事项】:
大家在测试@repeatable的代码时,别忘了配置gradle哈。要加上下边这个配置才可以:
在这里插入图片描述
即要加上:
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
如果不加的话,编译时会报错:
在这里插入图片描述
所以,切记这些小细节哈~
@Documented---用到的很少
顾名思义,这个元注解肯定是和文档有关。它的作用是能够将注解中的元素包含到 Javadoc 中去。ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举
4.注解的属性
注解的属性也叫做成员变量。
注解只有成员变量,没有方法。
【需要注意】的是:
-
在注解中定义属性时它的类型是有要求的。
只能用8种基本数据类型( byte,short,char,int,long,float,double,boolean),外加 类(Class),接口(interface),注解(annotation), String,以及这一些类的数组。
-
注解中属性可以有默认值,默认值需要用 default 关键值指定
如:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Test{ int id() default -1; String msg() default "Hello"; }
上面代码定义了 TestAnnotation 这个注解中拥有 id 和 msg 两个属性。
在使用的时候,我们应该给它们进行赋值。
赋值的方式是在注解的括号内以 value="" 形式,多个属性之前用 ,隔开:
@Test(id=1,msg="hello annotation") public class TestAnnotation { }
5.注解的提取
1.运行期注解(RunTime)---利用反射去获取信息。
2.编译期(Compile time)注解,以及处理编译期注解的手段是APT和Javapoet,@Retention(RetentionPolicy.CLASS)。
今天我们主要讲一下运行期注解的提取:
首先可以通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}
然后通过 getAnnotation() 方法来获取 Annotation 对象。
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}
或者是 getAnnotations() 方法。
public Annotation[] getAnnotations() {}
前一种方法返回指定类型的注解,后一种方法返回注解到这个元素上的所有注解。
如果获取到的 Annotation 如果不为 null,则就可以调用它们的属性方法了。比如
public class TestDemo {
public static void main(String[] args) {
boolean hasAnnotation = TestDemo.class.isAnnotationPresent(Test.class);
if (hasAnnotation) {
TestAnnotation testAnnotation = TestDemo.class.getAnnotation(Test.class);
System.out.println("id:" + testAnnotation.id());
System.out.println("msg:" + testAnnotation.msg());
}
}
}
6.注解的使用场景
- 提供信息给编译器: 编译器可以利用注解来探测错误和警告信息
- 编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
- 运行时的处理: 某些注解可以在程序运行的时候接受代码的提取
值得注意的是,注解不是代码本身的一部分。
积累点滴,做好自己~