注解
格式
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}
注解的参数类似无参数方法,可以用default设定一个默认值(强烈推荐)。最常用的参数应当命名为value。
如果没有设置 default 则必须传递
//定义带成员变量注解MyTag
@Rentention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTag{
//定义两个成员变量,以方法的形式定义
String name();
int age() default 20;
}
//使用
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。
// 单个注解
@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)这个元注解:
@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)
// 获取Person定义的@Report注解:
Report report = Person.class.getAnnotation(Report.class);
int type = report.type();
String level = report.level();
使用反射API读取Annotation有两种方法。方法一是先判断Annotation是否存在,如果存在,就直接读取:
Class cls = Person.class;
if (cls.isAnnotationPresent(Report.class)) {
Report report = cls.getAnnotation(Report.class);
// ...
}
第二种方法是直接读取Annotation,如果Annotation不存在,将返回null
Class cls = Person.class;
Report report = cls.getAnnotation(Report.class);
if (report != null) {
...
}
子类对象可能需要用到父类继承下来的东西,所以那些东西必须要先完成,所以父类构造函数必须在子类提前中完成
每个子类的构造函数会调用父类的构造函数,如此一致往上直到 object, 等 object 结束后执行剩下的构造函数
静态方法与非静态方法的区别
在某些特殊情况下,不需要用到类的实例,不受实例影响,相当于一个namespace
final
- final变量:一旦给值,就不能修改
- final方法:不能被子类的方法覆盖,但可以被继承
- final类:不能被继承,没有子类