Java在JDK5之后引入了"注解"的概念,实际上就是为了起到简化代码、易于理解的作用,并且通过注解也可以在编译时期发现一些代码格式的错误,更方便的进行开发。
注:这里只是简单记录下 Java 注解的相关概念,未详细讨论。且不涉及 Java 8 新加入的注解。后续会更新补充…
注解的声明类似于 接口 的声明,但稍有不同
// 声明注解使用 @interface 关键字
public @interface Test {
String name() default "";
}
一、元注解
元注解就是标记其他注解的注解
- @Target 约束注解应用的范围,取值范围为 ElementType 的枚举。(未指定 Target 值时,默认可用于以下任何类型)
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
}
- @Retention 约束注解的生命周期,取值范围为 RetentionPolicy 的枚举
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文件中有效,运行时无效(默认类型)
*/
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 将被修饰的注解生成到 javadoc 中
标记后使用 javadoc xxx.java 命令生成 javadoc 文件 - @Inherited 让子类对象通过反射获取父类被 @Inherited 修饰的注解
二、Java内置注解
- @Override 标明此方法覆盖了父类的方法
- @Deprecated 标明已经过时的方法或类
- @SuppressWarnningsz 关闭编译器警告(接受字段如下)
deprecation:使用了不赞成使用的类或方法时的警告;
unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型;
fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告;
path:在类路径、源文件路径等中有不存在的路径时的警告;
serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告;
finally:任何 finally 子句不能正常完成时的警告;
all:关于以上所有情况的警告。
三、注解可使用的数据类型
- 8种基本数据类型(byte、short、int、long、float、double、char、boolean)
- String
- Class
- enum
- Annotation(注解本身)
- 数组
默认值:编译器对注解要求是 要么在注解声明是指定默认值,要么在使用注解时赋值。不能存在不确定的值。
快捷方式:注解中自定义了 value的元素,当且仅当注解中只有一个需要赋值的元素时,可以直接赋值,不须使用“key=value”的形式。如:
@Retention(RetentionPolicy.RUNTIME)
也可使用
@Retention(value = RetentionPolicy.RUNTIME)
四、注解与反射
当注解被定义了 @Retention(RetentionPolicy.RUNTIME) 时,我们可以在代码中通过反射获取注解信息,从而进行我们想要的操作。(因为只有在 RUNTIME 时注解在运行期是有效的)
相关方法:
方法名 | 返回值 | 说明 |
---|---|---|
getAnnotation(Class<A> annotationClass) | <A extends Annotation> | 该元素如果存在指定类型的注解,则返回该注解,否则返回 null。 |
getAnnotations() | Annotation[] | 返回标注在此元素上的所有注解,包括父类注解 |
isAnnotationPresent(Class<? extends Annotation> annotationClass) | boolean | 指定类型注解是否标注在此元素上 |
getDeclaredAnnotations() | Annotation[] | 返回标注在此元素上的所有注解,不包括父类注解 |
下面实现一个使用注解的例子:
书店里有很多种类型的书,为了方便查看,将书的相关信息存放在注解中,通过反射获取其内容
- 定义一个 Book 注解,将书的相关属性进行定义(由于注解要存在于运行时,这里设置 RetentionPolicy.RUNTIME)
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Book {
/**
* 书名
*/
String name();
/**
* 价格
*/
double price();
/**
* 备注
*/
String remarks() default "";
}
- 定义一个 书店 类,进行配置书籍的相关信息
public class BookShop {
@Book(name="文学", price=30)
private String literature;
@Book(name="科幻", price=40, remarks="限时8折")
private String scienceFiction;
@Book(name="体育", price=22, remarks="新版上架")
private String sports;
}
- 通过反射获取信息
public class Main {
public static void main(String[] args) throws Exception {
Class<?> cls = BookShop.class;
// 获取属性元素
Field[] fields = cls.getDeclaredFields();
// 遍历属性元素,获取书籍注解的内容
for (Field field : fields) {
// 获取Book注解
Annotation annotation = field.getAnnotation(Book.class);
if (annotation != null && annotation instanceof Book) {
Book book = (Book) annotation;
// 取值
StringBuilder builder = new StringBuilder();
builder.append(book.name()).append("\t")
.append(book.price()).append("\t")
.append(book.remarks());
System.out.println(builder.toString());
}
}
}
}
输出结果:
文学 30.0
科幻 40.0 限时8折
体育 22.0 新版上架
注:在细分的话也可根据书籍类型划分多种类,每一个类下有多种图书,再声明一个书籍分类的注解即可。