一、什么是注解
概念: 说明程序的,给计算机看的。
注释: 用文字描述程序,给程序员看的。
Annotation是JDK1.5开始引入的新特性
使用: 可以标注在方法、变量、参数和包等
作用: 可以被其它程序,比如编译器读取
格式: 以@注释名在代码中存在,可以添加参数值
例: @SupperssWarnings(value=“unchecked”)
获取: 通过反射机制变成实现对这些元数据的控制
二、内置注解
- @Override:定义在 java.lang.Override中,此注释可以用于修饰方法,表示一个方法声明打算重写超类中的另一个方法声明
- @Deprecated: 定义在java.lang.Deprecated中,可以用于修饰方法,属性,类,表示不鼓励使用这样的元素,通常是它很危险,或者有更好的选择
- @SupperssWarnings:定义在java.lang.SupperssWarnings中,用来抑制编辑时的警告信息,与前两个注解不同,需要添加额外的参数才能使用
- @SupperssWarnings(“all”)
- @SupperssWarnings(“uncheked”)
- @SupperssWarnings(value={“uncheked”,“deprecation”})
- …
import java.util.Date;
public class Main {
//@Deprecated修饰该方法时,表示该方法不建议被使用
@Deprecated
public static void test() {
System.out.println("Deprecated Method");
}
public static void test1() {
System.out.println("Normal Method");
}
public static void testDate() {
Date date = new Date();
//Date是时间/日期类,java已不建议使用该类
System.out.println(date.getYear());
}
public static void main(String[] args) {
test();
test1();
testDate();
}
}
-
test() 被 @Deprecated 标注,意味着建议不再使用 test(); test() 的定义和调用时,都会一横线。这一横线是编译器对 @Deprecated 方法的处理。test1() 没有被 @Deprecated 标注,它的显示正常。
-
testDate() 调用了 Date 的相关方法,而 java 已经建议不再使用 Date 操日期/时间。因此,在调用 Date的API 时,会产生警告信息,途中的warnings。
三、自定义注解
注解的定义很像接口的定义,并且与其他任何Java接口一样,注解也将会编译成class文件。
//元注解
public @interface MyAnnotation {
}
编译和反编译:
本质:
public interface MyAnnotation extends java.lang.annotation.Annotation {
}
通过编译和反编译可以看出,通过@interface定义的注解,它的本质是个接口,所以,我们得出结论,注解本质就是要一个接口,该接口默认继承Annotation 接口。
属性:接口中的抽象方法
- 属性的返回值类型
- 基本数据类型
- String类型
- 枚举
- 注解
- 以上类型的注解
public @interface MyAnnotation {
int age();
String name() default "张三";
String[] str();
}
@MyAnnotation(age=15,str={"张三","李四"})
class Test{
}
定义了属性,在需要时给属性赋值
- 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
2.如果只有一个属性需要赋值,并且属性的名称时value,则value可以省略,直接定义值即可
3.数组赋值时,值使用{}包裹,如果数组中只有一个值,则{}可以省略
四、元注解
用于描述注解的注解
首先看一下@Override注解
@Target(ElementType.METHOD) //作用的位置,在方法上
@Retention(RetentionPolicy.SOURCE)//保留到什么时候,保留到源码中
public @interface Override {
}
@Target:表示该注解可以用到什么地方。
参数 | 说明 |
---|---|
CONSTRUCTOR | 构造器的声明 |
FIELD | 域声明(包括enum实例) |
LOCAL_VARIABLE | 局部变量声明 |
METHOD | 方法声明 |
PACKAGE | 包声明 |
PARAMETER | 参数声明 |
TYPE | 类、接口(包括注解类型)或enum声明 |
@Retention:表示需要在什么级别保存该注解信息。
参数 | 说明 |
---|---|
SOURCE | 注解将被编译器丢弃 |
CLASS | 注解在class中可用,但会被JVM丢弃 |
RUNTIME | JVM将在运行期也保留注解,因此可以通过反射机制读取注解的信息 |
@Documented:描述注解是否被抽取到api文档中
@Inherited:允许子类继承父类中的注解
Target源码
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
Target里只有一个ElementType数组,那ElementType是什么呢?查看源码
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
我们发现ElementType 是个枚举,那怎么用呢
当我们定义@Target(ElementType.TYPE)表示MyAnnotation注解只能作用于类上,在方法上不能使用该注解
当然,ElementType是个数组,可以定义多个值,就像这样
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@interface MyAnnotation{
}
其它元注解就不一一细说了
当我们编写好我们的注解后如果没有用来读取注解的工具的话,那么注解对于我们来说也就没有太大意义了。在Java SE5扩展了反射机制的API,以帮助程序员构造这类工具。
先定义一个简单的注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Person{
String name() default "I don't have name";
int age() default 21;
}
注解用于一个实体类中:
class MyAnnotation {
@Person(name = "My name is zhy")
public String zhy(){
return "zhy";
}
@Person(name = "My name is xyx", age = 19)
public String xyx(){
return "xyx";
}
}
通过反射机制查找注解中的信息
public class MyAnnotationTest {
public static void test(List<Integer> ages, Class<?> cl){
Method[] methods = cl.getDeclaredMethods();
for(Method method : methods){
Person person = method.getAnnotation(Person.class);
if(person != null){
System.out.println("My name is " + person.name() + " and I'm " + person.age());
ages.remove(new Integer(person.age()));
}
}
for(int i : ages){
System.out.print("Missing age is " + i);
}
}
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
Collections.addAll(list, 20, 21, 22);
test(list, MyAnnotation.class);
}
}
输出:
My name is My name is zhy and I'm 21
My name is My name is xyx and I'm 19
Missing age is 20Missing age is 22
在这个注解处理器程序中,我们用到了两个反射的方法:getDeclaredMethods()和getAnnotation(),这两个都是AnnotatedElement接口(Class、Method和Field等类都实现了该接口)。getAnnotation()方法返回指定类型的注解对象,在这里就是Person。如果被注解的方法上没有该类型的注解,则返回null值。然后我们通过调用name()和age()方法从Person对象中提取元素的值。
总结
以上就是java注解的使用,通过一个简单的案例来说明注解的使用再合适不过了,对于注解的理解在我们平常的使用过程中也能更加得心应手。