震惊,居然因为注解的这些小知识没用通过字节跳动的面试!

Q1:什么是注解,他们的典型用例是什么
Q2:请说出有哪些常用注解
Q3:怎么自定义注解
Q3.1:注解方法中可以返回哪些类型
Q3.2:怎么限制注释的元素对象
Q3.3:自定义注解怎么实现我们想要的逻辑功能(简要描述主要思想)
Q4:说一下什么是重复注解
Q5:说一下注解的继承关系,它是怎么实现的
Q6:定义一个@Getter 和 @Setter注解,他们只能修饰成员变量。为这两个注解编写APT工具,APT工具会为他们修饰的成员变量对应添加getter、setter方法。

使用注解的优势

之前总是用大量的xml来实现描述元数据,但是xml太过于繁琐和松耦合。在有些情况下我们希望使用一些较为简单的方式来描述元数据,来达到我们快速开发的目的,还有些特殊情况,我们希望用紧耦合的一下东西来描述元数据。这就用到了注解,现在大多数的框架都是xml配饰和注解相结合使用,平衡这两种方式的利弊。

常用注解

  • java.lang包下的五个常用注解

  • @override

    • 标记注解
    • 只能修饰方法
    • 告诉编译器检查这个方法,父类一定要有一个被该方法重写的方法,否则会报错
  • @Deprecated

    • java9增强获得两个属性
      • forRemoval:该Boolean类型的属性指定该API将来是否会被删除
      • since:该String类型的属性指定API从那个版本被标记过期
    @Deprecated
    public void deprecated(){
           
    
    }
    
    
    new Appel().deprecated();// 编译器警告显示该方法过期
    
    
  • @SuppressWarnings

    • 备注解修饰的元素及其子元素,取消显示编译器警告
  • @SafeVarargs

    • 标记注解
    • 消除堆污染警告,当把一个不带泛型的对象赋给一个带泛型的对象,往往会发生堆污染
  • @Functionallnterface

    • 和override类似
    • 告诉编译器,该接口是一个函数式的接口(一个接口中只有一个抽象方法 -> 可以使用拉姆达表达式)

提取注解的信息

  • getAnnotation(Class annotation)
    • 返回该程序元素上存在的指定类型的注释,如果该类型的注释不存在,则返回null
  • Annotation[] getAnnotations()
    • 返回该程序元素上存在的所有注释
  • boolean isAnnotationPresent(Class)
    • 判断该程序元素上是否包含指定类型的注释,存在则返回true,否则返回false

jdk的元注解

  • 元注解就是注解注解的注解,在我们自定义注解的时候使用
  • java.lang.annnotation包下的6个Meta注解,其中四个常用注解
  • @Retention
    • 只能用于修饰注解定义
    • 指定被修饰的注解可以保留多少时间
    • 包含一个RetentionPolicy类型的value的成员变量
    • 三个value的值
      • RetentionPolicy.CLASS
        • 默认值
        • 注解记录在class文件中
        • 运行java程序时,jvm不可获取注解信息
      • RetentionPolicy.RUNTIME
        • 注解记录在class文件中
        • 运行java程序时,jvm也可以获取注解信息
        • 程序可通过反射获取该注解
      • RetentionPolicy.SOURCE
        • 注解值保留在源代码中,编译器直接丢弃这种注解
  • @Target
    • 指定被修饰的注解作用于哪些程序单元
    • 8个value的值
      • ElementType.ANNOTATION_TYPE
        • 指定该注解只能修饰注解
      • ElementType.CONSTRUCTOR
        • 修饰构造器
      • ElementType.FIELD
        • 修饰成员变量
      • ElementType.LOCAL_VARIABLE
        • 修饰局部变量
      • ElementType.METHOD
        • 修饰方法
      • ElementType.PACKAGE
        • 修饰包
      • ElementType.PARAMETER
        • 修饰参数
      • ElementType.TYPE
        • 修饰类 、接口(包括注解累些)、枚举
  • @Decumented
    • 指定被该元注解修饰的注解类将被javadoc工具提取成文档
    • 使用该注解修饰的程序元素的API文档中将会包含该注解说明
  • @Inherited
    • 指定被修饰的注解具有继承性
    • 使用该注解的子类也会被该注解修饰

自定义注解

  • 使用@interface关键字
  • 可以设定参数
  • 可以给参数设定默认值
  • 注解分类
    • 标记注解
      • 没有定义成员变量的注解
      • 仅利用自身的存在与否来提供信息
    • 元数据注解
      • 包含成员变量的注解
    @Retention(RetentionPolicy.RUNTIME) // jvm可以获得参数信息
    @Target(ElementType.METHOD) // 该注解是执行在方法上面的
    @Documented //可以被提取为文档
    public @interface Annotation1 {
           
        String colour() default "red";
        int time() default 20;
    }
    
    

提取注解信息

  • 注解不会自动生效,需要开发者提取响应的工具来提取并处理注解信息
  • java.lang.annotation.Annotation
    • 接口
    • 表示程序元素前面的注解
    • 所有注解的父类
  • java.lang.reflect.Annotation
    • 接口
    • 主要实现类
      • Class
      • Constructor
      • Field
      • Method
      • Package
    • 通过发射获取AnnotatedElement对象后,可以调用该对象的几个方法访问注解信息
      • getAnnotation …
    Annotation[] annotations = Class.forName("day01.Fruit").getMethod("deprecated").getAnnotations();
        for (Annotation an : annotations){
           
            System.out.println(an);
        }
    

java8的重复注解

  • 在8之前不予许在同一个程序元素上使用重复类型的注解,8之后可以
  • 自定义重复注解
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    @Documented
    public @interface Annotation1 {
           
        String colour() default "red";
        int time() default 20;
    }
    
    
    //重复注解
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    @Documented
    public @interface RepeatAnnotation {
           
        Annotation1[] param();  //注解参数类型类型是另外一个注解
    }
    

java8新增的类型注解

java8为ElementType枚举增加了TYPW_PARAMETER、TYPE_USE两个枚举类,这样就允许定义枚举是使用@Targer(ElementType.TYPE_USE)修饰,这种注解别称为类型注解,类型注解课用于修饰在任何地方出现的类型。
类型注解可以让编译器更严格的代码检查,从而提高程序的健壮性

//自定义一个类型注解
@Target(ElementType.TYPE_USE)
public @interface NotNull {
     
}

//类型注解可以修饰任何地方出现的类型
@NotNull
public class TypeAnnotation implements @NotNull Serializable {
     

    public static void main(String[] args) {
     
        Object obj = "Hellolvzi";

        String str = ( @NotNull String) obj;
    }
    public void foor(Class<? extends @NotNull String> info){
     

    }

}

编译时处理注解

该内容涉及到了反射,在反射章节会来解决这个问题

注解的继承关系

所有的注解都是继承与java.lang.annotation.Annotation

public @interface aa {
     
}

但是在我们自定义注解的时候并没有写extends,来继承这个Annotation。但是在看annotation继承关系的时候(idea atrl + h 打开继承树 ,可以看我的这篇博客自己工作总结常用快捷键),我们看到这我们的注解继承与annotation
震惊,居然因为注解的这些小知识没用通过字节跳动的面试!_第1张图片
那么它是怎么实现的呢?
如果你想不到不用extends确有继承关系,那么你一定不理解为什么Object是所有类的父类。
请移步我的这篇文章,之前写过不想在粘贴麻烦各位了。
同理,我们自定义注解的继承关系也是在编译阶段完成的,当识别到@interface,编译器会给你添加一个默认的父类Annotation,而不是object

许多知识都是相互联系的,最好的记忆方法就是把所有的知识形成一个框架

你可能感兴趣的:(java,java)