Skip to content
On this page

注解

格式

java
public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

注解的参数类似无参数方法,可以用default设定一个默认值(强烈推荐)。最常用的参数应当命名为value。

如果没有设置 default 则必须传递

java
//定义带成员变量注解MyTag
@Rentention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) 
public @interface MyTag{
  //定义两个成员变量,以方法的形式定义
  String name();
  int age() default 20;
}
java
//使用
public class Test{
  @MyTag(name="test")
  public void info(){}
}

元注解

有一些注解可以修饰其他注解,这些注解就称为元注解(meta annotation)。Java标准库已经定义了一些元注解,我们只需要使用元注解,通常不需要自己去编写元注解。

@Target

最常用的元注解是@Target。使用@Target可以定义Annotation能够被应用于源码的哪些位置:

  • 类或接口:ElementType.TYPE;
  • 字段:ElementType.FIELD;
  • 方法:ElementType.METHOD;
  • 构造方法:ElementType.CONSTRUCTOR;
  • 方法参数:ElementType.PARAMETER。
java
// 单个注解
@Target(ElementType.METHOD)
public @interface Report {
  // 。。。。
}


// 多个在注解使用数组
@Target({
    ElementType.METHOD,
    ElementType.FIELD
})
public @interface Report {
   // 。。。。
}

@Retention

另一个重要的元注解 @Retention 定义了 Annotation 的生命周期:

  • 仅编译期:RetentionPolicy.SOURCE;
  • 仅class文件:RetentionPolicy.CLASS;
  • 运行期:RetentionPolicy.RUNTIME。

如果@Retention不存在,则该Annotation默认为CLASS。因为通常我们自定义的Annotation都是RUNTIME,所以,务必要加上@Retention(RetentionPolicy.RUNTIME)这个元注解:

java
@Retention(RetentionPolicy.RUNTIME)
public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

如何使用注解完全由工具决定。
SOURCE类型的注解主要由编译器使用,因此我们一般只使用,不编写。
CLASS类型的注解主要由底层工具库使用,涉及到class的加载,一般我们很少用到。
只有RUNTIME类型的注解不但要使用,还经常需要编写。

因为注解定义后也是一种class,所有的注解都继承自java.lang.annotation.Annotation,因此,读取注解,需要使用反射API

使用反射API读取Annotation:

  • Class.getAnnotation(Class)
  • Field.getAnnotation(Class)
  • Method.getAnnotation(Class)
  • Constructor.getAnnotation(Class)
java
// 获取Person定义的@Report注解:
Report report = Person.class.getAnnotation(Report.class);
int type = report.type();
String level = report.level();

使用反射API读取Annotation有两种方法。方法一是先判断Annotation是否存在,如果存在,就直接读取:

java
Class cls = Person.class;
if (cls.isAnnotationPresent(Report.class)) {
    Report report = cls.getAnnotation(Report.class);
     // ...
}

第二种方法是直接读取Annotation,如果Annotation不存在,将返回null

java
Class cls = Person.class;
Report report = cls.getAnnotation(Report.class);
if (report != null) {
   ...
}

子类对象可能需要用到父类继承下来的东西,所以那些东西必须要先完成,所以父类构造函数必须在子类提前中完成

每个子类的构造函数会调用父类的构造函数,如此一致往上直到 object, 等 object 结束后执行剩下的构造函数

静态方法与非静态方法的区别

在某些特殊情况下,不需要用到类的实例,不受实例影响,相当于一个namespace

final

  • final变量:一旦给值,就不能修改
  • final方法:不能被子类的方法覆盖,但可以被继承
  • final类:不能被继承,没有子类