前言
本文介绍内容如下:
- 概念
- 5个基本注解
- jdk元注解
- 自定义注解
一、注解
- Annotation:其实是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用注解,开发人员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充的信息。
- 注解本质:是一个接口,程序可以通过反射来获取指定程序元素的java.lang.annotation.Annotation对象,然后通过java.lang.annotation.Annotation对象来获取注解里的元数据。
- 注解是在jdk5后引入的
注解的作用:
- 不是程序本身,可以对程序作出解析。可以被其他程序(比如编译器等)读取。
注解的格式:
注解是以“@注释名”在代码中存在的,还可以添加一些参数值,例如:@SuppressWarnings(value=“unchecked”)。
二、基本注解
- @Override
- @Deprecated
- @SuppressWarnings
- @SafeVarags
- @FunctionalInterface
2.1 @Override:限定重写父类方法
- 存在意义:避免犯低级错误,方法名写错等
public class People {
public void info(){
System.out.println("吃饭,睡觉");
}
}
class Teacher extends People{
@Override
public void info() {
System.out.println("教书");
}
}
2.2 @Deprecated
- 表示程序元素(类、方法)等已过时,当其他程序使用已知的类、方法时,编译器将会发出警告。警告方式为将该方法名划掉
2.3 @SuppressWarnings:镇压警告
- 可以取消编译器警告。
- @SuppressWarnings(value=“unchecked”)
2.4 @SafeVarags与堆污染?
List list = new ArrayList<>();
list.add(20);//添加元素时引发unchecked异常
//未经检查的转换的警告,编译,运行时完全正常
List<String> ls=list;
//只要访问ls里面的元素,如下面代码就会引发运行时异常
System.out.println(ls.get(0));
- 引发这种错误的原因称为“堆污染”,当把一个不带泛型的对象赋给一个带泛型的变量时,往往会发生这种“堆污染”,
- 此时,虽然可以使用镇压警告,但还是建议在处理堆污染的时候使用@SafeVarags,Java7出现,专门抑制堆污染警告。Java9增强了该注解,允许使用该注解修饰私有实例方法。
Java8的函数式接口
- 如果接口中只有一个抽象方法,该接口就是函数式接口,@FunctionalInterface就是用来指定某个接口必须是函数式接口。
@FunctionalInterface
public interface FunInterface {
void test();
void test2();
}
- 在上述接口中存在两个函数式接口,编译会爆出下面错误。
Error:(7, 1) java: 意外的 @FunctionalInterface 注释
com.example.demo.FunInterface 不是函数接口
在 接口 com.example.demo.FunInterface 中找到多个非覆盖抽象方法
三、JDK元注解
3.1 使用@Retention
- @Retention只能用于修饰注解定义,用于指定被修饰的注解可以保留多长时间,@Retention包含一个RetentionPolicy类型的value成员变量,所以使用2Retention时必须为该value衡阳变量指定值。
- value的成员变量:
- RetentionPolicy.CLASS:编译器将把注解记录在class文件中。当运行程序时,JVM不可获取注解信息,这是默认值。
- RetentionPolicy.RUNTIME:编译器把注解记录在class文件中,当运行时,JVM可获取注解信息,程序可以通过反射来获取注解信息。
- RetentionPolicy.SOURCE:保留在源代码中,编译器直接丢弃该注解
- 总结:如果需要通过反射获取注解信息,就需要使用value属性值为 RetentionPolicy.RUNTIME的。
@Retention(value = RetentionPolicy.RUNTIME)
public @interface a();
3.2 使用Target
- @Target也只能修饰注解定义,它用于指定被修饰的注解能用于修饰哪些程序单元。@Target元注解也包含一个名为value的成员变量,该成员变量的值只能是如下几个。
3.3 使用Documented
- @Documented用于指定被该元注解修饰的注解类将被javadoc工具提取成文档,如果定义注解类时使用了@Documented修饰,则所有使用该注解修饰的程序元素的api文档中将会包含该注解说明。
3.4 使用@Inherited
- @Inherited元注解指定被它修饰的注解将具有继承性,如果某个类使用了@Xxx注解,则其子类时将自动被@Xxx修饰。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Inheritable {
}
@Inheritable
public class Base {
}
class BaseSon extends Base{
public static void main(String[] args) {
System.out.println(Base.class.isAnnotationPresent(Inheritable.class));
}
}
四、自定义注解
4.1 自定义元注解
public class Test2 {
@MyAnnotation
public void test(){
}
}
/**
* 定义一个注解
* Target:表示我们的注解可以用在哪些地方
* Retention:表示我们的注解可以用在哪些地方
* Documented:表示是否将我们的注解生成在java文档中
* Inherited:子类可以继承父类的注解
* runtime>class>source
*/
@Target(value = {ElementType.METHOD,ElementType.TYPE})//约束,在方法层使用
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Inherited
@interface MyAnnotation
}
4.2 自定义注解使用
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Author: XiaoLei
* @Date Created in 14:14 2020/2/7
* 自定义注解
*/
public class Test3 {
@MyAnnotation2(name = "潇雷",schools = "华东师范大学")
public void test(){
}
@MyAnnotation3("潇雷")
public void test2(){
}
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
//注解的参数:参数类型+参数名();
String name() default "";
int age() default 0;
int id() default -1;//如果默认值为-1代表不存在
String[] schools();
// String name() ; 没有默认值就要自己重写
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
String value();//只有一个属性的时候,建议用value,然后在备注注解时可以直接使用内容,而不需要写value=多少
}
4.3 注解与配置文件
- 配置文件:
- 优点:可配置,不用改源码。 -缺点:不直观,开发效率低
- 注解: -优点:直观,开发效率高 -缺点:硬编码,修改之后需要重新编译运行
4.4 注解使用注意事项
-
- 要用好注解,必须熟悉Java 的反射机制,从上面的例子可以看出,注解的解析完全依赖于反射。
-
- 不要滥用注解。平常我们编程过程很少接触和使用注解,只有做设计,且不想让设计有过多的配置时,才需要考虑注解。