1.注解定义
也叫元数据,一种代码级别的说明,可以声明在包、类、字段、方法、局部变量、方法参数上,对这些元素进行说明,注释。
2.作用:
a.编写文档:通过代码里标识的元数据生成文档【生成javadoc文档】
例如@param,@author,@version
b.代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
c.编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】
例如@Override覆盖重写,@Deprecated标记已过时,@SuppressWarnings压制警告,@FunctionalInterface (java8支持)标识一个匿名函数或函数式接口
d.跟踪代码依赖性:实现替代配置文件功能,比较常见的是spring 2.5 开始的基于注解配置,作用就是减少配置;
3.注解分类
3.1 JAVA自带的标准注解
3.1.1 @Override
如果试图使用 @Override 标记一个实际上并没有覆写父类的方法时,java 编译器会告警。
class Parent {
public void test() {
}
}
class Child extends Parent {
/**
* 放开下面的注释,编译时会告警
*/
/*
@Override
public void test() {
}
*/
}
3.1.2 @Deprecated
用于标明被修饰的类或类成员、类方法已经废弃、过时,不建议使用。
@Deprecated
class TestClass {
// do something
}
3.1.3 @SuppressWarnings
用于关闭对类、方法、成员编译时产生的特定警告。
抑制单类型警告
@SuppressWarnings("unchecked")
public void addItems(String item){
@SuppressWarnings("rawtypes")
List items = new ArrayList();
items.add(item);
}
抑制多类型警告
@SuppressWarnings(value={"unchecked", "rawtypes"})
public void addItems(String item){
List items = new ArrayList();
items.add(item);
}
3.1.4 @FunctionalInterface
用于指示被修饰的接口是函数式接口,在 JDK8 引入
@FunctionalInterface
public interface UserService {
void getUser(Long userId);
// 默认方法,可以用多个默认方法
public default void setUser() {
}
// 静态方法
public static void saveUser() {
}
@Override
// 覆盖Object中的equals方法
public boolean equals(Object obj);
}
函数式接口,也就是只有一个抽象方法的接口。上面的代码只有一个抽象方法getUser,不会报错,再增加一个抽象方法后就会报错,如下图:
3.2 元注解
注解注解的注解
3.2.1 @Retention
用来定义该注解在哪一个级别可用,在源代码中(SOURCE)、类文件中(CLASS)或者运行时(RUNTIME)
Retention源代码:
翻译指示带注释接口的注释要保留多长时间。如果注释接口声明上不存在Retention注释,则保留策略默认为RetentionPolicy.CLASS。
只有当元注释接口直接用于注释时,保留元注释才有效。如果元注释接口被用作另一个注释接口中的成员接口,则没有任何效果。
REtentionPolicy源代码
翻译SOURCE:注释将由编译器丢弃。
CLASS:注释将由编译器记录在类文件中,但不需要在运行时由VM保留。这是默认行为。
RUNTIME:注释由编译器记录在类文件中,并在运行时由VM保留,因此可以反射式读取。
3.2.2 @Documented
生成文档信息的时候保留注解,对类作辅助说明
@Documented
public @interface DocumentedMy {
String value() default "这是一个自定义类";
}
@DocumentedMy
public class MyTest1 {
public String getStr() {
return "123";
}
}
生成javadoc效果
3.2.3 @Target
用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
public class MyInterface2 <@TypeParam T>{
@Not long lon;
@Test
public <@TypeParam T extends Integer> void Test2(@Not String str){
@Not boolean boo;
}
@Test
public <@TypeParam T extends Integer> void Test1(){
}
}
/**
* jdk8为@Target元注解新增了两种类型:TYPE_PARAMETER TYPE.USE
* TYPE_PARAMETER:表示该注解能写在类型参数的声明语句中,泛型 例如:<@TypeParam T>
* TYPE.USE:表示可以在任何用到类型的地方使用
*
**/
@Target(ElementType.TYPE_PARAMETER)
@interface TypeParam{
}
@Target(ElementType.TYPE_USE)
@interface Not{
}
![1D9U~%04NN}SUA8@E`)Z~5.png
标记了package的注解,并不是直接在类中的package上添加注解,而是在pakage-info中进行添加
3.2.4 @Inherited
说明子类可以继承父类中的该注解
表示自动继承注解类型。 如果注解类型声明中存在 @Inherited 元注解,则注解所修饰类的所有子类都将会继承此注解。
注解zhujie被@Inherited修饰
test类上添加该注解
test1类继承test类
然后获取test1类上的注解zhujie,发现可以获取到,说明test类上的注解zhujie被继承到test1类上了
3.2.5 @Repeatable
表示注解可以重复使用。
如果注解没有@Repeatable,在同一个地方使用相同的注解会报错,有了此元注解注解的注解,就可以在同一个地方使用相同的注解。
使用示例
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Values.class)
public @interface Value {
String value() default "value";
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Values {
Value[] value();
}
同一个地方使用相同注解就不会报错了
会将这两个value注解装进Values集合中。
一些约束:
1.@Repeatable 所声明的注解,其元注解@Target的使用范围要比@Repeatable的值声明的注解中的@Target的范围要大或相同,否则编译器错误,显示@Repeatable值所声明的注解的元注解@Target不是@Repeatable声明的注解的@Target的子集
2.@Repeatable注解声明的注解的元注解@Retention的周期要比@Repeatable的值指向的注解的@Retention得周期要小或相同。
3.3 自定义注解
3.3.1 格式
元注解:
pubulic @interface 注解名称{
属性
}
3.3.2 属性
即接口中的抽象方法
要求:
第一,在定义注解时,不能继承其他的注解或接口。
第二,只能用 public 或 默认(default) 这两个访问权修饰.例如,String value();这里把方法设为 defaul 默认类型;
第三,参数成员只能用基本类型 byte,short,char,int,long,float,double,boolean 八种基本数据类型 和 String,Enum,Class,annotations 等数据类型,以及这一些类型的数组.例如,String value();这里的参数成员就为String;
第四,如果只有一个参数成员,最好把参数名称设为"value",这样在使用注解时就可以从
value = “XXX” 省略为 “XXX”。
第五,定义了属性,在使用时需要给属性赋值,定义属性时可以使用default进行赋值
3.3.3 在程序中使用(解析)注解
1.获取注解存在的类的class对象
a.Class.forName(String className),多用于配置文件,将全类名写在配置文件中,读取配置文件生成Class对象。
b.类名.class,多用于参数传递,比如需要传递某个类的Class对象。
c.对象.getClass
2.根据注解所在位置(类,方法,变量),通过反射获取该对象
1.获取构造器
构造器作用:通过构造器创建对象
如果是获取的私有构造器,那么使用该构造器创建对象前需要使用暴力反射,即构造器.setAccessible(ture)
2.获取成员变量
getFields()
getField(String name)
getDeclaredFields()
getDeclaredField(String name)
成员变量作用:通过成员变量取值与赋值:
注意:成员变量不能孤立存在,需要有对象依托!
如果是私有变量在赋值之前也需要进行暴力反射
3.获取成员方法
成员方法作用:通过成员方法运行方法
注意:成员方法执行时也需要对象进行依托!
方法也需要和对象进行绑定,如果是私有方法也需要setAccessible(ture)来暴力反射
3.使用对象.getAnnotation()获取注解对象
4.使用注解对象的抽象方法获取注解配置的属性值