Java注解,又称Java标注,是JDK1.5(Java5)引入的一种新机制,用于为 Java 代码提供元数据。
一、自定义注解
@interface
使用 @interface 定义时,即定义了一个注解(Annotation)。
注解不能继承其他的注解或接口。
public @interface Nickname {
//注解中是没有方法的,只有成员变量。
//成员变量名后面要加括号。
String name();
boolean isAdult() default false;
}
二、元注解
元注解的作用就是在自定义注解上添加注解,是对自定义注解的说明。
JDK1.5中定义了四个标准的元注解:
1. @Target
定义注解的使用范围。取值有以下几种:
ElementType | 使用范围 |
---|---|
TYPE | 类、接口(包括注解类型) 、枚举 |
FIELD | 类成员变量(包括枚举中的常量) |
METHOD | 函数 |
PARAMETER | 函数的形参 |
CONSTRUCTOR | 构造函数 |
LOCAL_VARIABLE | 局部变量 |
PACKAGE | 包 |
ANNOTATION_TYPE | 注解类型 |
TYPE_PARAMETER | (JDK1.8开始)类、接口,枚举的参数 |
TYPE_USE | (JDK1.8开始)类、接口或枚举的使用。应用于任何使用类型的语句中(例如声明语句、泛型和强制转换语句中的类型)。 |
例:
@Target ( { ElementType.METHOD, ElementType.TYPE } )
public @interface Nickname {
......
}
2. @Retention
定义注解的保存范围。取值有以下几种:
RetentionPolicy | 保存范围 |
---|---|
SOURCE | 源代码(java文件)级别保留,编译时被忽略。 |
CLASS(默认) | 编译时被保留(即,在class文件中存在),JVM被忽略。 |
RUNTIME | JVM被保留。能在运行时被JVM或其他使用反射机制的代码所读取和使用。所以如果需要读取注解中的值,需要将@Retention设置为RUNTIME。 |
例:
@Target ( { ElementType.TYPE } )
@Retention ( RetentionPolicy.RUNTIME )
public @interface Nickname {
......
}
3. @Documented
注解默认是不出现在 javadoc 中的。
如果用 @Documented 修饰该注解,则表示该注解会出现在 javadoc 中。
@Documented是一个标记注解,没有成员。
例:
@Target ( { ElementType.TYPE } )
@Retention ( RetentionPolicy.RUNTIME )
@Documented
public @interface Nickname {
......
}
4. @Inherited
如果一个使用了@Inherited修饰的注解被用于一个类,那么这个注解也被用于该类的子类。
使用@Inherited的注解,只有在类上使用时有效,对函数、属性等都无效。
例:
@Target ( { ElementType.TYPE } )
@Retention ( RetentionPolicy.RUNTIME )
@Inherited
public @interface Nickname {
......
}
5. @Repeatable(JDK1.8加入)
被@Repeatable修饰的注解可以同时作用一个对象多次。
使用@Repeatable的方法:
(1)在需要重复使用的注解(注解①)上修饰:@Repeatable(参数)
(2)@Repeatable的参数为:注解的容器的class对象(注解②)
(3)注解②包含一个value方法,返回一个注解①的数组
例:
(1)需要重复使用的注解(Nickname):
package study.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target ({ElementType.TYPE, ElementType.METHOD})
@Retention ( RetentionPolicy.RUNTIME )
@Repeatable ( Nicknames.class ) // Repeatable修饰(参数为:注解Nickname的容器Nicknames)
public @interface Nickname {
String name() default "Tony";
boolean isAdult() default false;
}
(2)注解的容器(Nicknames):
package study.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target ({ElementType.TYPE, ElementType.METHOD})
@Retention ( RetentionPolicy.RUNTIME )
public @interface Nicknames {
Nickname[] value(); // 容器包含一个value方法,返回一个注解Nickname的数组
}
(3)通过反射的方法获取注解数组:
public class TestAnnotation {
public static void main(String[] args) throws Exception {
try {
ClassB b = new ClassB();
Nickname[] names = b.getClass().getMethod("FuncB").getAnnotationsByType(Nickname.class);
for (Nickname name : names) {
System.out.println("name:" + name.name());
System.out.println("adult:" + name.isAdult());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
三、自定义注解的使用
(1)定义注解:Nickname
package annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target ({ElementType.TYPE})
// 要获取注解,那么自定义注解的保存范围必须设置为RUNTIME
@Retention ( RetentionPolicy.RUNTIME )
public @interface Nickname {
String name() default "Tony";
boolean isAdult() default false;
}
(2)类ClassA使用注解Nickname
package annotation;
@Nickname(name="Jane")
public class ClassA {
}
(3)解析注解
java.lang下提供了一些函数用于反射注解:
函数 | 功能 | 参数 | 返回值 |
---|---|---|---|
java.lang.Class.isAnnotationPresent | 判断是否被某注解修饰 | Class:注解的类型 | boolean |
java.lang.Class.getAnnotation | 返回指定注解 | Class:注解的类型 | Annotation:注解 |
java.lang.Class.getAnnotations | 返回所有注解 | 无 | Annotation[]:包含所有注解的数组 |
注意:要获取注解,那么自定义注解(例如上面的Nickname)的保存范围必须设置为RUNTIME:@Retention(RetentionPolicy.RUNTIME)
package annotation;
import java.lang.annotation.Annotation;
public class TestAnnotation {
public static void main(String[] args) throws Exception {
try {
ClassA a = new ClassA();
Class<? extends ClassA> clazz = a.getClass();
if (clazz.isAnnotationPresent(Nickname.class)) {
System.out.println("ClassA has Nickname.");
Nickname nick = (Nickname) clazz.getAnnotation(Nickname.class);
System.out.println("ClassA.name = " + nick.name());
System.out.println("ClassA.isAdult = " + nick.isAdult());
Annotation[] annos = clazz.getAnnotations();
} else {
System.out.println("ClassA has no Nickname.");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
(4)运行结果:
【完】