注解
注解的概念
Annotation 注解 可以看成是java中的一种标记记号,用来给java中的类,成员,方法,参数等任何程序元素添加一些额外的说明信息,同时不改变程序语义。注解是用来告诉编译器一些事情,例如使用@Override注解,可以告诉编译器这个父类方法被子类方法覆盖了。
注解也称元数据,在层次上与类,接口,枚举同一层级(在这里我们可以称这些都是类型)。其声明是用@interface,类似于类用class,接口用interface, 枚举用enum。是用来修饰程序元素的“对象”。所有的注解都要实现java.lang.annotation.Annotation接口。
注解在某些情况下可以让JVM在对类编译形成.class文件时给类中的程序元素添加一些说明信息,然后在通过反射技术对这些说明信息进行动态获取,这在后面的框架学习中有很大的用处,这可以让我们在不改变任何代码逻辑的情况下,对代码的行为进行“配置”。
注解的分类
注解可以分成三大类:基本注解,元注解和自定义注解
元注解
元注解也称为元数据注解,是对注解进行标注的注解,元注解更像是一种对注解的规范说明,用来对定义的注解进行行为的限定。例如说明注解的生存周期,注解的作用范围等。java中定义的元注解共有5种:
-
@Target(value=“作用范围”)
该注解是用来限制注解的使用范围的,即该注解可以用于哪些程序元素,其作用范围的可选值为java.lang.annotation.ElementType枚举类型中定义的值中,如下所示:
作用范围 说明 ElementType.PACKAGE 该注解只能用在包上 ElementType.TYPE 该注解只能用在类,接口或枚举类型的声明上 ElementType.ANNOTATION_TYPE 该注解只能用在注解类型上 ElementType.CONSTRUCTOR 该注解只能用在构造方法上 ElementType.METHOD 该注解只能用在实例方法上 ElementType.FIELD 该注解只能用在成员变量上 ElementType.LOCAL_VARIABLE 该注解只能用在局部变量上 ElementType.PARAMETER 该注解只能用在参数声明上 还有两个在1.8版本之后新添加的枚举值:TYPE_PARAMETER和TYPE_USE,类型注解,可以用在任何用到类型的地方,除了类,对象,还可用在类型转化,使用throws声明抛出异常等等。
举例说明:
在我们的Junit测试中常用的Test注解的定义如下:
@Target({ElementType.METHOD})
public @interface Test {
...
}
//-----------------
public class Demo{
//用在方法上
@Test
public void test1() {
...
}
}
说明了@Test注解只能用在实例方法中
-
@Retention(value=“保存的策略值”)
用于说明注解的生存周期,例如保存在源码中,还是可以保存进.class字节码文件中。保存的策略值的可选范围在java.lang.annotation.RetentionPolicy的枚举类型中被定义,如下所示:
保存策略值 说明 SOURCE 注解会被编译器忽略 CLASS 注解会被保存进字节码文件中,但是在执行程序的时候并不会加载该注解的信息,是默认值 RUNTIME 注解会被保存进字节码文件,程序运行的时候会被加载注解信息,常和反射机制一起使用 其中被使用的最多的应该就是RUNTIME,毕竟其可以在程序运行的时候获取注解的信息,当配合反射机制一起使用的时候就可以动态的配置一些文件了。而另外两个倒不是很常见。
-
@Documnent
用来说明指定被修饰的注解可以被javadoc.exe工具提取进入文档中,所有使用了该注解进行标注的类在生成API文档时都在包含该注解的说明。
-
@Inherited
用来说明使用了该注解的父类,其子类会自动继承该注解。
-
@Repeatable
java1.8新出的元注解,如果需要在给程序元素使用相同类型的注解,则需将该注解标注上。
基本注解
基本注解有5个,这是java官方给我们定义好的,并使用了元注解进行修饰,全都在java.lang包中,可以用在所有的程序元素中。
-
@Deprecated
该注解用来说明程序中的某个元素(类,方法,成员变量等)已经不再使用,如果使用的话的编译器会给出警告,如果在IDEA中我们使用了这些方法它也会将这些方法划掉,给我们提示。例如java.util包下的Date类中的很多方法都被添加进了该注解,表示不建议再使用了。
@Deprecated
public Date(int year, int month, int date) {
this(year, month, date, 0, 0, 0);
}
因此现在日期并不推荐使用Date类,可以使用日历类Calander进行替换。
值得一提的是:使用@Deprecated注解修饰的方法不是说方法的算法本身有什么错误,而是要么已经不使用了,有更好的推荐方法使用,**要么就是该算法在某些环境下可能会导致错误的发生。**java官方也没有把这些代码删掉,就留在那里了。
-
@SuppressWarnings(value=“警告参数”)
suppress 就是抑制的意思,顾名思义,就是用来已经编译器警告的产生,即不允许警告提示信息的出现,可以用在各种程序元素中。其取值有如下几种,用来抑制各种可能出现的警告:
警告参数 | 说明 |
---|---|
unchecked | 忽略参数类型没有转换的警告 |
unused | 忽略定义了没有使用变量的警告 |
boxing | 忽略数据类型进行装箱/拆箱时的警告 |
finally | 忽略finally语句不能正常完成时的警告 |
all | 忽略所有的警告 |
fallthrough | 忽略switch语句中没有break语句的警告 |
path | 忽略在源文件路径,类文件路径中有不存在路径的警告 |
serial | 忽略实现了Serializable接口,但是没有定义serialVersionUID常量的警告 |
rawtypes | 忽略使用泛型,但是没有限制类型的警告 |
deprecation | 忽略使用了@Deprecated注解的元素的警告 |
-
@Override
这应该是我们学习java最早见到的一个注解了,该注解即是用来说明子类方法覆盖了父类的方法,保护覆盖方法的正确使用~
-
@FunctionalInterface
用来说明一个接口时函数式接口,关于函数式接口的内容可以请看:…
-
@SafeVarargs
这个注解学到现在并没有遇到过,说是用来抑制“堆污染”警告,所谓的堆污染指的是将一个不带泛型的对象赋给了一个带泛型的对象,导致了泛型对象的污染,简单的例如下面这个例子:
List list = new ArrayList(); list.add(10); List<String> list1 = new ArrayList<String>(); list1 = list;
注意,这个注解和上面的@suppressWarnings注解一样都是用来抑制警告的,但是它额外定义了一个注解;
自定义注解
上面说了注解和类,接口,枚举类型属于同一层次(都是类型),我们可以使用java内部给我们定义好的(就是上面的元注解和基本注解),也可以自己定义注解,只需要使用@Interface来声明注解,使用方法如下:
public @Interface 注解名 {
数据类型 成员变量名() default “默认值”;
}
注意到这里成员变量名后面有个**()**这和我们类中的定义有点不一样,类中的有个括号的是叫做成员方法,但是这里就是这么定义的。
例如springMVC框架中自定义的@RequestMapping注解:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String name() default "";
String[] value() default {};
RequestMethod[] method() default {};
String[] params() default {};
String[] headers() default {};
String[] consumes() default {};
String[] produces() default {};
}
又如我们自己定义的一个Info类型的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Info {
String author() default "smrobot";
String value() default "";
}
定义了一个Info类型,用在类型中的注解,其里面有个成员变量author默认值是smrobot,可以配置如下:
@Info(author="小机")
public class Test {
...
}
在程序运行中就可以去除author的值了。
注意,如果有名为value成员变量,我们在使用该注解时可以不写上该成员变量的名称,例如上面的那个自定义注解:
@Info(value="小机")
public class Test {
...
}
//--------------不写value----
@Info("小机")
public class Test {
...
}//这样value的值也仍然为“小机”
总结:虽然有很大的可能我们不会自己定义注解,而是调用别人写好的注解,例如调用spring框架的,mybatis框架的。但是为了后面更好的理解框架的机理和反射机制,还是在此记录一下~