1、注解的概念
(1)注解的定义
注解是一种能被添加到Java代码中的元数据,类、方法、变量、参数和包都可以用注解修饰。注解对于它修饰的代码并没有直接影响。
(2)注解的使用范围
为编译器提供信息。
编译器和部署时的处理。
运行时的处理。
2、注解的语法
(1)声明部分
public @interface MyAnnotation {
}
使用@interface声明为注解类型;在底层,所有注解都会自动继承java.lang.annotation.Annotation接口。
(2)实现部分
public @interface MyAnnotation {
public String name();
int age() default 18;
int[] array();
}
注解内部实现定义的是注解类型元素。
定义注解类型元素有以下要求:
- 访问修饰符必须为public,不写默认为public;
- 该元素的类型只能是基本数据类型、String、Class、枚举类型、注解类型(注解可以相互嵌套)以及以上数据的数组;
- 该元素的名称一般定义为名词,如果注解中只有一个元素,请把名字起为value,更加便于使用;
- 注解中的()不能定义任何参数,定义的也并非是方法,只是语法要求;
- default为默认值,与对应元素类型一致;
- 若没有默认值,代表使用注解时,必须对该类型元素进行赋值。
3、元注解
元注解:专门定义注解的注解。它们是为了实现自定义注解而专门是设计的。
(1)@Target
该注解定义自定义注解能够使用在哪些元素上。
该类型元素为一个枚举类型,定义为java.lang.annotation.ElementType。
public enum ElementType {
/** 类,接口(包括注解)或枚举声明 */
TYPE,
/** 属性或枚举常量的声明 */
FIELD,
/** 方法声明 */
METHOD,
/** 方法参数声明 */
PARAMETER,
/** 构造函数声明 */
CONSTRUCTOR,
/** 局部变量声明 */
LOCAL_VARIABLE,
/** 注解类型声明 */
ANNOTATION_TYPE,
/** 包声明,只能用在package-info.java文件中*/
PACKAGE,
/**
* 类型参数声明,即泛型方法、泛型类、泛型接口
* @since 1.8
*/
TYPE_PARAMETER,
/**
* 可以声明任意类型除了Class
* @since 1.8
*/
TYPE_USE
}
(2)@Retention
该注解定义自定义注解的声明周期。该类型元素为java.lang.annotation.RetentionPolicy。
public enum RetentionPolicy {
/**
* Java源文件阶段,注解会被编译器忽略。
*/
SOURCE,
/**
* 编译到Class文件阶段,注解将被编译到class中,但在运行时会被虚拟机忽略。
*/
CLASS,
/**
* 运行阶段,不仅会编译到class文件中,还会被虚拟机识别,通过反射进行读取。
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
- 如果一个注解被定义为RetentionPolicy.SOURCE,则它将被限定在Java源文件中,那么这个注解即不会参与编译也不会在运行期起任何作用,这个注解就和一个注释是一样的效果,只能被阅读Java文件的人看到;
- 如果一个注解被定义为RetentionPolicy.CLASS,则它将被编译到Class文件中,那么编译器可以在编译时根据注解进行处理,但是运行时JVM(Java虚拟机)会忽略它,在运行期也不能读取到;
- 如果一个注解被定义为RetentionPolicy.RUNTIME,那么这个注解可以在运行期的加载阶段被加载到Class对象中。在程序运行阶段,我们可以通过反射得到这个注解,并通过判断是否有这个注解或这个注解中属性的值,从而执行不同的程序代码段。实际开发中的自定义注解几乎都是使用的RetentionPolicy.RUNTIME;
- 在默认的情况下,自定义注解是使用的RetentionPolicy.CLASS。
(3)@Documented
该注解是用来被指定自定义注解是否随被定义的Java文件生成到JavaDoc文档中。
(4)@Inherited
该注解指定自定义注解能够被子类继承。该注解只能针对@Target(ElementType.TYPE)的自定义注解起作用。
4、特殊语法
(1)若注解没有注解类型元素,那么是可以省略()
例如:@Documented()等价于@Documented
(2)若注解本身只有一个注解类型元素,而且命名为value,在使用注解时可以直接使用@注解名(注解值)
例如:@Retention(value = RetentionPolicy.RUNTIME)等价于@Retention(RetentionPolicy.RUNTIME)
(3)若注解中某个注解类型元素是一个数组类型,在使用时又只需要填入一个值,那么可以直接使用@注解名(类型名 = 类型值)
例如:@Target(value = {ElementType.TYPE})等价于@Target(value = ElementType.TYPE)等价于@Target(ElementType.TYPE)
5、使用自定义注解
共分为三个阶段
1.定义注解
2.配置注解
3.解析注解
(1)定义注解
使用上面介绍的元注解,定义我们想要的自定义注解
//代码
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FieldAnnotation {
//判断字段是否为空
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MethodAnnotation {
String value() default "this is a method";
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface ParameterAnnotation {
//获取参数的Class
}
@Target(ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
@Documented()
public @interface TypeAnnotation {
public String desc() default "this a class";
}
(2)配置注解
根据要只用的自定义注解,我们把它配置在合适的位置上。
@TypeAnnotation
public class Demo {
@FieldAnnotation
private String name;
private int size;
@MethodAnnotation(value = "getSomeThing...")
public void getSomeThing(@ParameterAnnotation String param) {
System.out.println("getSomeThing");
}
public void saySomeThing(String param) {
System.out.println("saySomeThing");
}
}
(3)解析注解
通过反射获取在已配置的注解和其注解类型元素配置的值。
public class Client {
public static void main(String[] args) {
Demo demo = new Demo();
Class clz = demo.getClass();
TypeAnnotation typeAnnotation = (TypeAnnotation) clz.getAnnotation(TypeAnnotation.class);
System.out.println(typeAnnotation.desc());
Method[] methods = clz.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(MethodAnnotation.class)) {
MethodAnnotation methodAnnotation = method.getAnnotation(MethodAnnotation.class);
System.out.println(methodAnnotation.value());
}
method.getParameterAnnotations();
}
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(FieldAnnotation.class)) {
FieldAnnotation fieldAnnotation = field.getAnnotation(FieldAnnotation.class);
System.out.println(field.getName());
}
}
}
}
有关获取注解的反射方法介绍。
- isAnnotationPresent(Class<? extends Annotation> annotationClass),该方法判断该元素上是否配置某个指定注解。
- getAnnotation(Class< T > annotationClass),该方法获取元素上指定的注解,随之可以获取到注解类型元素上配置的值。
- getAnnotations(),该方法获取元素上配置的所有注解,返回的为注解数组。
样例:通过自定义注解导出Excel
1、定义注解
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String name();
int width();
//TODO 对字段进行排序
int sort() default 0;
}
2、配置注解
public class Person {
@Column(name = "姓名",width = 15)
private String name;
@Column(name = "年龄",width = 10)
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
3、解析注解
public class ExcelUtils {
private final static float CORRECTION_PARAMETER = 0.72F;
public static <T extends Object> void exportExcel(List<T> data, Class<T> clz) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException,
InvocationTargetException, IOException {
//1.创建一个webbook,对应一个Excel文件
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet("Sheet1");
//2.第一行表头
HSSFRow headRow = sheet.createRow(0);
Field[] fields = clz.getDeclaredFields();
//设置表头格式
HSSFCellStyle headCellStyle = workbook.createCellStyle();
Font fontStyle = workbook.createFont();
fontStyle.setBold(true);
// fontStyle.setFontHeightInPoints((short) 12);
headCellStyle.setFont(fontStyle);
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (field.isAnnotationPresent(Column.class)) {
Column column = field.getAnnotation(Column.class);
Cell cell = headRow.createCell(i);
cell.setCellValue(column.name());
cell.setCellStyle(headCellStyle);
sheet.setColumnWidth(i, (int) ((column.width() + CORRECTION_PARAMETER) * 256));
}
}
//3.第二行开始遍历数据
for (int i = 0; i < data.size(); i++) {
HSSFRow row = sheet.createRow(i + 1);
//逐列填充
for (int j = 0; j < fields.length; j++) {
Object object = data.get(i);
Field field = fields[j];
if (field.isAnnotationPresent(Column.class)) {
String fieldName = field.getName();
String firstLetter = fieldName.substring(0, 1).toUpperCase();
String getter = "get" + firstLetter + fieldName.substring(1);
Method method = object.getClass().getMethod(getter, new Class[]{});
Object value = method.invoke(object, new Object[]{});
row.createCell(j).setCellValue(value.toString());
}
}
}
workbook.write(new FileOutputStream("D:/log/table.xls"));
}
}