1. 元注解
元注解:用在注解上的注解,java1.5后添加的4个元注解:
- @Target
- @Retention
- @Documented
- @Inherited
在java1.8又添加了两个注解:
- @Native
- @Repeatable
先说明下这几个注解.
@Target 修饰的对象范围
取值(ElementType)有:
- 1.CONSTRUCTOR:用于描述构造器
- 2.FIELD:用于描述域
- 3.LOCAL_VARIABLE:用于描述局部变量
- 4.METHOD:用于描述方法
- 5.PACKAGE:用于描述包
- 6.PARAMETER:用于描述参数
- 7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
当注解类型声明中没有@Target元注解,则默认为可适用所有的程序元素。
@Retention 注解保留到哪个时期(生命周期)
取值(RetentionPoicy)有:
- 1.SOURCE:在源文件中有效(即源文件保留)
- 2.CLASS:在class文件中有效(即class保留)
- 3.RUNTIME:在运行时有效(即运行时保留)
默认CLASS,可以看下源码中提解释:
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
@Documented 标志可文档化
被标注的程序成员的公共API,因此可以被例如javadoc此类的工具被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化
@Inherited 将注解内容传递到子类
若是方法被重写则得不到父类方法中的注解内容,关于该方法所有的内容都被重新定义了.
在类上的注解会被传递该子类,方法也可以但重写了则不会在传递给子类
再次捋清楚下:
- 如果父类的注解是定义在类上面,那么子类是可以继承过来的
- 如果父类的注解定义在方法上面,那么子类仍然可以继承过来
- 如果子类重写了父类中定义了注解的方法,那么子类将无法继承该方法的注解
- 即子类在重写父类中被@Inherited标注的方法时,会将该方法连带它上面的注解一并覆盖掉
2.常见的Java注解
我们平时直接使用java提供几个注解:@Override @Deprecated @SuppressWarnings @FunctionalInterface,下面逐个看下其定义
-
@Override :告知服务器,我们要覆盖父类中的当前方法.
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
-
@Deprecated :告知编译器,某一程序元素不建议使用了
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) public @interface Deprecated { }
-
@SuppressWarnings 忽略特定的警告
用于告知编译器忽略特定的警告信息,例在泛型中使用原生数据类型,编译器会发出警告,当使用该注解后,则不会发出警告。@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); }
可以接受多个参数例如:
@SupressWarning(value={"uncheck","deprecation"})接受的类型:
- all: to suppress all warnings
- boxing: to suppress warnings relative to boxing/unboxing operations
- cast: to suppress warnings relative to cast operations
- dep-ann: to suppress warnings relative to deprecated annotation
- deprecation: to suppress warnings relative to deprecation
- fallthrough: to suppress warnings relative to missing breaks in switch statements
- finally: to suppress warnings relative to finally block that don’t return
- hiding: to suppress warnings relative to locals that hide variable
- incomplete-switch: to suppress warnings relative to missing entries in a switch statement (enum case)
- nls: to suppress warnings relative to non-nls string literals
- null: to suppress warnings relative to null analysis
- rawtypes: to suppress warnings relative to un-specific types when using generics on class params
- restriction: to suppress warnings relative to usage of discouraged or forbidden references
- serial: to suppress warnings relative to missing serialVersionUID field for a serializable class
- static-access: to suppress warnings relative to incorrect static access
- synthetic-access: to suppress warnings relative to unoptimized access from inner classes
- unchecked: to suppress warnings relative to unchecked operations
- unqualified-field-access: to suppress warnings relative to field access unqualified
- unused: to suppress warnings relative to unused code
-
@FunctionalInterface 保证该接口是函数式接口
用户告知编译器,检查这个接口,保证该接口是函数式接口,即只能包含一个抽象方法,否则就会编译出错。@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface FunctionalInterface {}
3. 自定义注解
Annotaion不影响程序代码的执行,无论增加、删除Annotation,代码都始终如一地执行。
也就是说:如果我们不去解析注解,它就没什么作用和影响.
在 java.lang.Class 中末尾有4个涉及的解析注解的方法:
-
<T extends Annotation> T getAnnotation(Class<T> annotationClass)
返回改程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
-
Annotation[] getAnnotations()
返回该程序元素上存在的所有注解。
-
boolean isAnnotationPresent(Class<?extends Annotation> annotationClass)
判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.
-
Annotation[] getDeclaredAnnotations()
返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。
除此之外还有写注意事项:
- 成员类型受限:原始类型,String,Class,Annotation,Enumeration
- 注解类只有一个成员时,必须取名为value(),在使用时可以忽略成员名和赋值号(=)
- 注解类可以没有成员,称之为标识注解
4. 实践自定义注解
首先看下接下来要实现的相关类:
FruitName 水果名称
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FruitName {
String value() default "";
}
FruitColor 水果颜色
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FruitColor {
public enum Color {
BLUE("蓝色"), RED("红色"), GREEN("绿色");
private String name;
public String getName() {
return name;
}
Color(String name) {
this.name = name;
}
}
Color value() default Color.GREEN;
}
FruitProvider 供应商信息
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface FruitProvider {
int id() default 1;
String address() default "";
String providerName() default "";
}
接下来到使用类了
Apple 使用注解
public class Apple {
@FruitName("苹果")
public String name;
@FruitColor(FruitColor.Color.GREEN)
public String color;
@FruitProvider(id=1001,address = "美国硅谷",providerName = "苹果公司")
public String provider;
}
FruitAnnotationParser 注解解析类
public class FruitAnnotationParser {
public static void parse() {
Field[] fields = Apple.class.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(FruitName.class)) {
FruitName fruitName = field.getAnnotation(FruitName.class);
String value = fruitName.value();
System.out.println(value);
} else if (field.isAnnotationPresent(FruitColor.class)) {
FruitColor fruitColor = field.getAnnotation(FruitColor.class);
FruitColor.Color color = fruitColor.value();
System.out.println(color.getName());
} else if (field.isAnnotationPresent(FruitProvider.class)) {
FruitProvider provider = field.getAnnotation(FruitProvider.class);
String address = provider.address();
int id = provider.id();
String providerName = provider.providerName();
System.out.println(String.format("providerName:%s id:%d address:%s", providerName, id, address));
}
}
}
}
测试
public class FruitTest {
public static void main(String[] args) {
FruitAnnotationParser.parse();
}
}
输出:
苹果
绿色
providerName:苹果公司 id:1001 address:美国硅谷
注解理解之后使用起来还是比较简单的,解析注解的套路就那么4个,解析的过程都差不多.