1. 内置注解
-
@Override
@Override修饰一个方法,表示该方法不是当前类首先声明的,而是在某个父类或实现的接口中声明的,当前类“重写”了该方法。
-
@Deprecated
@Deprecated可以修饰的范围很广,包括类、方法、字段、参数等,它表示对应的代码已经过时了,程序员不应该使用它,不过,它是一种警告,而不是强制性的。
-
@SuppressWarnings
@SuppressWarnings表示压制Java的编译警告,它有一个必填参数,表示压制哪种类型的警告
1.1 注解的本质
没错,注解的本质就是一个继承了 Annotation 接口的接口。有关这一点,你可以去反编译任意一个注解类,你会得到结果的。
一个注解准确意义上来说,只不过是一种特殊的注释而已,如果没有解析它的代码,它可能连注释都不如。
1.2 元注解
元注解是用于修饰注解的注解,通常用在注解的定义上,JAVA 中有以下几个元注解:
Target
Target注解用来说明那些被它所注解的注解类可修饰的对象范围:注解可以用于修饰 packages、types(类、接口、枚举、注解类)、类成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数),在定义注解类时使用了@Target 能够更加清晰的知道它能够被用来修饰哪些对象,它的取值范围定义在ElementType 枚举中。
public enum ElementType {
TYPE, // 类、接口、枚举类
FIELD, // 成员变量(包括:枚举常量)
METHOD, // 成员方法
PARAMETER, // 方法参数
CONSTRUCTOR, // 构造方法
LOCAL_VARIABLE, // 局部变量
ANNOTATION_TYPE, // 注解类
PACKAGE, // 可用于修饰:包
TYPE_PARAMETER, // 类型参数,JDK 1.8 新增
TYPE_USE // 使用类型的任何地方,JDK 1.8 新增
}
Retention注解
Reteniton注解用来限定那些被它所注解的注解类在注解到其他类上以后,可被保留到何时,一共有三种策略,定义在RetentionPolicy枚举中。
public enum RetentionPolicy {
SOURCE, // 源文件保留
CLASS, // 编译期保留,默认值
RUNTIME // 运行期保留,可通过反射去获取注解信息
}
Documented
Documented注解的作用是:描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息。
Inherited
Inherited注解的作用是:使被它修饰的注解具有继承性(如果某个类使用了被@Inherited修饰的注解,则其子类将自动具有该注解)。
1.3 注解的工作原理
首先,我们通过键值对的形式可以为注解属性赋值,像这样:@Hello(value = “hello”)。
接着,你用注解修饰某个元素,编译器将在编译期扫描每个类或者方法上的注解,会做一个基本的检查,你的这个注解是否允许作用在当前位置,最后会将注解信息写入元素的属性表。
然后,当你进行反射的时候,虚拟机将所有生命周期在 RUNTIME 的注解取出来放到一个 map 中,并创建一个 AnnotationInvocationHandler 实例,把这个 map 传递给它。
最后,虚拟机将采用 JDK 动态代理机制生成一个目标注解的代理类,并初始化好处理器。
那么这样,一个注解的实例就创建出来了,它本质上就是一个代理类,你应当去理解好 AnnotationInvocationHandler 中 invoke 方法的实现逻辑,这是核心。一句话概括就是,通过方法名返回注解属性值。
2. 创建注解
@Target(ElementType.METHOD) // 注解的目标
@Retention(RetentionPolicy.SOURCE) // 表示注解信息保留到什么时候(源代码,字节码,运行时)
public @interface xxx{
}
3. 查看注解信息
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
static @interface QueryParam{
String value();
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
static @interface DefaultValue{
String value() default "";
}
public void hello(@QueryParam("action") String action, @QueryParam("sort") @DefaultValue("asc") String sort){
}
public static void main(String[] args) throws NoSuchMethodException {
Class<?> cls = MethodAnnotations.class;
Method clsMethod = cls.getMethod("hello", new Class[]{String.class, String.class});
Annotation[][] annts = clsMethod.getParameterAnnotations();
for(int i = 0; i < annts.length; ++i){
System.out.println(i+1);
Annotation[] anntArr = annts[i];
for(Annotation annt: anntArr){
if(annt instanceof QueryParam){
QueryParam qp = (QueryParam) annt;
System.out.println(qp.annotationType().getSimpleName()+":"+ qp.value());
}else if(annt instanceof DefaultValue){
DefaultValue dv = (DefaultValue)annt;
System.out.println(dv.annotationType().getSimpleName()+":"+ dv.value());
}
}
}
}
4. 注解的使用:定制序列化
// test
public class test{
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Format{
String pattern() default "yyyy-MM-dd HH:mm:ss";
String timezone() default "GMT+8";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Label{
String value() default "";
}
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Getter
@Setter
static class Student{
@Label("姓名")
String name;
@Label("出生日期")
@Format(pattern = "yyyy/MM/dd")
Date born;
}
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Student zhangsan = new Student("张三", sdf.parse("1990-12-12"));
System.out.println(SimpleFormatter.format(zhangsan));
}
}
// simpleFormatter
public class SimpleFormatter {
private static Object formatDate(Field f, Object value) {
SampleAnnotation.Format format = f.getAnnotation(SampleAnnotation.Format.class);
if (format != null) {
SimpleDateFormat sdf = new SimpleDateFormat(format.pattern());
sdf.setTimeZone(TimeZone.getTimeZone(format.timezone()));
return sdf.format(value);
}
return value;
}
public static String format(Object obj) {
try {
Class<?> cls = obj.getClass();
StringBuilder sb = new StringBuilder();
for (Field f : cls.getDeclaredFields()) {
if (!f.isAccessible()) {
f.setAccessible(true);
}
SampleAnnotation.Label label = f.getAnnotation(SampleAnnotation.Label.class);
String name = label != null ? label.value() : f.getName();
Object value = f.get(obj);
if (value != null && f.getType() == Date.class) {
value = formatDate(f, value);
}
sb.append(name + ":" + value + "\n");
}
return sb.toString();
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}