注解
注解的基本概念
- 注解(Annotation)又叫标注,是从java5开始增加的一种引用数据类型。跟枚举一样是从Java5开始增加的,枚举我们就看做是一种特殊类,注解就看做是一种特殊的接口即可。
- 注解本质上就是代码中的特殊标记,通过这些标记可以在编译、类加载、以及运行时执行指定的处理。就像超市的货架上的标签一样。就是对代码做一些特殊声明,以及代码补充之类的。
注解的语法格式
访问修饰符 @interface 注解名称 { @interface 就是在告诉我们注解是一种特殊的接口
注解成员;
}
- 自定义注解自动继承java.lang.annotatiob.Annotation接口。
- 通过@注解名称 的方式可以修饰包、类、成员方法、成员变量、构造方法、参数、局部变量的声明等。
注解的使用方式
- 注解体中只有成员变量没有成员方法,而注解的成员变量以"无形参的方法(无形参的抽象方法)"形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。
- 如果注解中只有一个参数成员,建议使用参数名为value,而类型只能是八种基本数据类型、String类型、Class类型、enum类型及Annotation类型。
元注解
- 元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。元注解不能应用在类上。
- 元注解主要有:@Retention、@Documented、@Target、@Inherited、@Repeatable。
- @Retention:保持的意思,主要描述的是注解的有效范围(生命周期)
- @Documented:主要描述的是这个注解是否在文档注释中体现
- @Target:目标的意思,表示这个注解到底可以修饰哪些内容
- @Inherited:是继承的意思,表示注解是否可以被继承到我所标记的类的子类中
- @Repeatable:是否可以重复。
元注解@Retention
- @Retention 应用到一个注解上用于说明该注解的生命周期,取值 如下:
- RetentionPolicy.SOURCE 注解只在源码阶段(.java)保留,在编译器进行编译时它将被丢弃忽视。
- RetentionPolicy.CLASS 注解只被保留到编译进行的时候(.class),它并不会被加载到 JVM 中(运行阶段就丢弃了),默认方式。
- RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载到 JVM 中,所以程序运行时可以获取到它们。
元注解@Documented
- 使用javadoc工具可以从程序源代码中抽取类、方法、成员等注释形成一个和源代码配套的API帮助文档,而该工具抽取时默认不包括注解内容。Idea是集成开发环境,已经不再需要使用控制台了,直接用导航栏的Tools–>Generate JavaDoc–>选择要生成文档的目标–>指定输出目录Output directory–> Other command line arguments: -encoding utf-8 指定其它命令行参数:编码格式为utf-8(防止中文乱码)–> Open generated documentation in browser(生成之后可以用浏览器打开) 注:输出目录最好不要有中文,代码存放路径绝对不能有中文。
- @Documented用于指定该注解将被javadoc工具成文档
- 定义为@Documented的注解必须设置Retention值为RUNTIME。
- 此处我们提取注释和注解到D:\LGStudy\javadoc中了
元注解@Target
- @Target 用于指定被修饰的注解能用于哪些元素的修饰,取值如下:
元注解@Inherited
- @Inherited 并不是说注解本身可以继承,而是说如果一个超类被该注解标记过的注解进行注解时,如果子类没有被任何注解应用时,则子类就继承超类的注解。
package com.lagou.module02.task05;
import java.lang.annotation.*; // *表示导入所有
/**
* @author hhc19
* @date 2022/1/2 11:03
* @description
*/
// @Retention(RetentionPolicy.SOURCE) // 表示下面注解在源代码中有效,一旦编译就没了
// @Retention(RetentionPolicy.CLASS) // 表示下面注解在字节码文件中有效,到运行阶段就没了,默认方式
@Retention(RetentionPolicy.RUNTIME) // 表示下面注解在运行时有效,在运行阶段依旧是有效、健在的
@Documented // 表示下面的注解信息可以被javadoc 工具提取到API文档中
// 表示下面的注解可以用于类型(类、接口、枚举)、构造方法、成员变量、成员方法、参数的修饰,不加该注解默认都有
@Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Inherited // 表示下面的注解所修饰的类中的注解可以被子类继承
// 若一个注解中没有任何的成员,则这样的注解叫做标记注解/标识注解
// 这个注解仅仅是打了一个标记/做了一个标识而已
public @interface MyAnnotation {
// ctr + shift + 回车 直接到下一行
public String value() default "默认值"; // 声明一个String类型的成员变量,名字为value, 这个在之前的学习中是定义了一个名为value的抽象方法
// public Direction ss();Error: Invalid type 'Direction' for annotation member 无效的类型,类型有要求
public String value2();
}
package com.lagou.module02.task05;
/**
* @author hhc19
* @date 2022/1/2 11:03
* @description
*/
// 表示将标签MyAnnotation贴在Person类的代码中,使用注解时采用 (成员参数名 = 成员参数值, ...) 的方式对注解中的成员进行初始化
/** 注解中不想给值的两种情况:
* 1、注解中没有任何的成员时(标记接口,标记注解);
* 2、如果有两个成员的时候还是不想给值:使用default关键字给成员变量给一个默认值,在使用注解的时候就可以不给值;
* 有默认值的时候也可以传值。
*/
// @MyAnnotation(value = "hello", value2 = "world")
@MyAnnotation(value2 = "world") // MyAnnotation not application to type MyAnnotation不能应用在类型上
// @Retention(RetentionPolicy.RUNTIME) 元注解只能标注在注解上。
public class Person {
/**
* name是用于描述姓名的成员变量
*/
@MyAnnotation(value2 = "1")
private String name;
/**
* age是用于描述年龄的成员变量
*/
private int age;
/**
* 编程实现无参构造方法
*/
@MyAnnotation(value2 = "2")
public Person() {
}
/**
* 编程实现有参构造方法
* @param name
* @param age
*/
public Person(@MyAnnotation(value2 = "4") String name, int age) {
this.name = name;
this.age = age;
}
/**
* 自定义成员方法实现特征的获取和修改
* @return 代表返回值的注解
*/
@MyAnnotation(value2 = "3")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.lagou.module02.task05;
/**
* @author hhc19
* @date 2022/1/2 14:15
* @description
*/
// 也就是可以继承Person类的注解,前提是Person类上标注的注解上标注了 @Inherited
public class Student extends Person {
}
元注解@Repeatable
- @Repeatable表示自然可重复的含义(这个注解可以被多次使用),从Java8开始增加的新特性。在一个类上多次重复使用。
- 从Java8开始对元注解@Target的参数类型ElementType枚举值增加了两个:
- 其中ElementType.TYPE_PARAMETER 表示该注解能写在类型变量的声明语句中,如泛型。
- 其中ElementType.TYPE_USE 表示该注解能写在使用类型的任何语句中。
package com.lagou.module02.task05;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Target;
/**
* @author hhc19
* @date 2022/1/2 14:20
* @description 自定义注解用于描述人物的角色
*/
// 下面的注解本来是不能实现多次使用的,要想多次使用实际上还是得依赖于ManTypes那种数组的形式,所以我们就把那个放数组的那个注解拿过来放这
// 但是此处需要一个Class对象,所以就.class,从此以后MainType注解就可以重复使用
// ManType制定了Target之后,在ManTypes中也要指定
@Repeatable(value = ManTypes.class) // Target of container annotation 'com.lagou.module02.task05.ManTypes' is not a subset of target of this annotation
@Target(ElementType.TYPE_USE)
public @interface ManType {
String value() default "";
}
package com.lagou.module02.task05;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
/**
* @author hhc19
* @date 2022/1/2 14:24
* @description 自定义注解里面可以描述多种角色
*/
@Target(ElementType.TYPE_USE)
public @interface ManTypes {
ManType[] value();
}
package com.lagou.module02.task05;
/**
* @author hhc19
* @date 2022/1/2 14:22
* @description
*/
// @ManTypes({@ManType("职工"), @ManType("超人")}) // 在Java8处理多个注解的方式,此时既满足了我们只用一次注解的方式,又可以使用两种值的形式
// Duplicate annotation. The declaration of 'com.lagou.module02.task05.ManType' does not have a valid java.lang.annotation.Repeatable annotation
// MainType并不是一个可重复的注解,本质上它还是依赖于ManTypes中的数组去处理
@ManType("职工")
@ManType("超人")
public class Man {
public static void main(String[] args) {
int ia = 97;
// char c1 = (@ManType char) ia; '@ManType\' not applicable to type use'
char c1 = (@ManType char) ia;
}
}
package com.lagou.module02.task05;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
/**
* @author hhc19
* @date 2022/1/2 15:05
* @description
*/
@Target(ElementType.TYPE_PARAMETER)
public @interface Test {
}
public static <@Test T> void print(T[] inputArray) {}
常见的预制注解
- 预制(预先制定)注解就是Java语言自身提供的注解,具体如下:
- 常用的预制注解如下:
@Override 限定重写父类的方法,该注解只能用于方法
@Deprecated 用于表示所修饰的元素(类、方法等)已过时
@SuppressWarnings 抑制编译器警告,一般用在eclipse中,idea用得很少。
@Deprecated // 表示该方法已经过时,不建议再使用了
public void show() {
System.out.println("这个方法马上过时了...");
}
public class ManTest {
public static void main(String[] args) {
Man man = new Man();
man.show(); // 'show()' is deprecated show方法已经过时了,不建议使用了
}
}