谈谈 java(5) : java中的注解技术

166 阅读5分钟

前言

本文介绍内容如下:

  1. 概念
  2. 5个基本注解
  3. jdk元注解
  4. 自定义注解

一、注解

  • 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的成员变量,该成员变量的值只能是如下几个。

image.png

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 注解使用注意事项

    1. 要用好注解,必须熟悉Java 的反射机制,从上面的例子可以看出,注解的解析完全依赖于反射。
    1. 不要滥用注解。平常我们编程过程很少接触和使用注解,只有做设计,且不想让设计有过多的配置时,才需要考虑注解。