Java 注解(Annotation)的简介与使用实例
2017-09-26 20:18
573 查看
注解和注释的联系与区别
注解和注释都有对目标(类、方法、参数等)进行补充说明的作用,它们的区别在于:注释只存在于类文件中,不参与编译;注解可以参与编译,也可以不参与编译。
注释内容仅供开发者和用户在编写程序时查看,对程序本身不会造成任何影响;注解可以在程序运行时利用反射读取,可以对程序本身造成影响。
java.lang中定义的三种注解
java.lang中定义了三种常用的注解:@Override:表示当前的方法定义将覆盖超类中的方法。如果把方法的名字拼错,或是参数列表与超类不一致(变成了重载),编译器就会发出错误提示。
@Deprecated:表示该方法已经被废弃。如果在代码中使用被@Deprecated注解的方法,编译器会发出警告。
@SuppressWarnings:关闭不当的编译器警告信息。
标记注解
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface DBTable { }
上面的代码段中就定义了一个最简单的自定义注解。这个注解中没有任何元素,因此被称为标记注解。可以看出,注解的定义和接口很像,仅仅是在interface关键字前面加上了一个@。与类和接口的不同的是,在创建注解时需要加上两个元注解:@Target与@Retention。元注解是Java系统提供的,负责新注解的创建。
@Target
@Target用于定义该注解将应用于什么地方,可以是类、方法、域、参数等等。它的取值(存在于枚举类ElementType中)有以下几种:TYPE:类、接口(包括注解)、枚举
FIELD:域、枚举实例
METHOD:方法
PARAMETER:参数声明
CONSTRUCTOR:构造器
LOCAL_VARIABLE:局部变量
ANNOTATION_TYPE:注解
PACKAGE:包
(Java 8)TYPE_PARAMETER:类型变量的声明。例:String str= new @NotNull String();
(Java 8)TYPE_USE:使用类型的任何语句(声明语句、泛型和强制转换语句中的类型)。例:myString = (@NonNull String) str;
@DBTable被@Target(ElementType.TYPE)标注,因此它可以被用在类、接口、枚举上。
@Retention
@Retention用于定义注解在哪个级别可用。它的取值(存在于RetentionPolicy枚举类中)有以下几种:SOURCE:注解会被编译器抛弃
CLASS:注解在class文件中可用,但会在运行时被VM抛弃
RUNTIME:注解在运行期也将被保留,可以利用反射读取
@DBTable被@Retention(RetentionPolicy.RUNTIME)标注,因此它可以存在于运行期。
注解的简单使用
@DBTable public class Table { ... }
这样就给Table类加上了@DBTable注解。可以看出,注解的使用几乎和其他修饰符(public、static等)相同。
现在,这个注解还没有任何意义,后面会演示它的作用。
为注解添加元素
注解中可以添加以下类型的元素:所有基本类型(int、long)
String
Class
enum
Annotation
以上类型的数组
下面为@DBTable注解添加一个String类型的tableName元素:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface DBTable { public String tableName() default ""; }
注意:元素名称后面要加上一对圆括号。这是因为注解本身是一种继承自接口java.lang.annotation.Annotation的特殊接口。
可以利用default为元素设置默认值,当使用注解时没有给出该元素的值时,会使用预先设置的默认值。如果元素没有提供默认值,那么在使用时就必须给定一个值,否则编译器会报错。
有元素的注解的使用
@DBTable(tableName = "Table1") public class Table { }
这样就将@DBTable中的tableName设置成了“Table1”。
如果注解有多个元素需要设置,只需用逗号分隔开:
@User(id = 100, name = "Bob")
如果某个元素是数组,那么需要加上一对大括号:
@SuiteClasses({ Test1.class, Test2.class, Test3.class })
上面的例子中还有一个特殊的地方:前面在为元素赋值时都使用“名 = 值”的模式,而这里没有显式标明元素名。实际上,这个是Java系统提供的一种“快捷方式”:如果注解中某个元素是唯一需要赋值的元素(唯一一个没有提供默认值的元素,或者是唯一一个元素),并且该元素值名为value,那么在赋值时就可以省去元素名与等号。
下面是SuiteClasses的定义,可以看到它只有一个元素,并且名称为value:
public @interface SuiteClasses { public Class<?>[] value(); }
注解处理器实例:根据JavaBean生成SQL语句
相比于注释,注解的最大优点就是可以在运行时通过反射读取其内容,这为编写各种工具类乃至框架提供了极大的便利。下面的例子中提供了一个工具类以及一系列注解,可以根据满足JavaBean规范的类动态地生成SQL语句。首先是使用到的自定义注解:
(1)表名注解@DBTable:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface DBTable { public String tableName() default ""; }
(2)整型注解@SQLInteger:
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface SQLInteger { String name() default ""; }
(3)字符串型注解@SQLString:
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface SQLString { public String name() default ""; public int length(); }
下面是注解处理器,基本都是通过反射读取注解内容实现。需要注意的一点是,JavaBean模式下所有域的访问权限都是private的,因此需要利用reflect包内的AccessibleObject类提供的方法获取其访问权限。
public class TableCreater { public static String printSQL(Class<?> clazz){ //判断clazz是否有DBTable注解,若没有则返回 DBTable dbTable; if(clazz == null || (dbTable = clazz.getAnnotation(DBTable.class)) == null) { return null; } //获得表名 String tableName = dbTable.tableName().length() < 1 ? clazz.getName().toUpperCase() : dbTable.tableName() .toUpperCase(); //获得所有域及其访问权限 Field[] fields = clazz.getDeclaredFields(); AccessibleObject.setAccessible(fields, true); //生成SQL语句CREATE TABLE中每一行的内容 List<String> columns = new LinkedList<>(); for(Field field : fields) { String column = null; Annotation[] annotations = field.getDeclaredAnnotations(); if(annotations.length < 1) { continue; } for(Annotation annotation : annotations) { if(annotation instanceof SQLInteger) { SQLInteger sInteger = (SQLInteger)annotation; String name = sInteger.name().length() < 1 ? field.getName().toUpperCase() : sInteger.name().toUpperCase(); column = name + " " + "INT"; }else if (annotation instanceof SQLString) { SQLString sString = (SQLString)annotation; String name = sString.name().length() < 1 ? field.getName().toUpperCase() : sString.name().toUpperCase(); String type = "VARCHAR(" + String.valueOf(sString.length()) + ")"; column = name + " " + type; } } if(column != null){ columns.add(column); } } //将表名和各行内容拼接成SQL语句 StringBuilder sql = new StringBuilder(); sql.append("CREATE TABLE "); sql.append(tableName); sql.append("(\n"); for(int i = 0 ; i < columns.size() ; i++){ sql.append(" "); sql.append(columns.get(i)); if(i < columns.size() - 1){ sql.append(','); } sql.append('\n'); } sql.append(")\n"); //返回结果 return sql.toString(); } }
下面看一个运行实例。首先是需要生成SQL语句的UserInfo类:
@DBTable(tableName = "USER") public class UserInfo { public UserInfo() { } @SQLString(length = 10) private String name; @SQLInteger private int age; @SQLString(length = 30) private String job; @SQLString(length = 30) private String email; 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; } public String getJob() { return job; } public void setJob(String job) { this.job = job; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
将UserInfo.class作为参数传入TableCreater.printSQL方法中:
public static void main(String[] args) { System.out.println(TableCreater.printSQL(UserInfo.class)); }
输出结果:
CREATE TABLE USER( NAME VARCHAR(10), AGE INT, JOB VARCHAR(30), EMAIL VARCHAR(30) )
@Documented与@Inherited
@Documented与@Inherited也是创建自定义注解时使用的元注解,它们的意义如下:@Documented:将此注解包含在Javadoc中。
@Inherited:允许子类继承父类的注解。
(Java 8)重复注解
一般的注解不能在一个目标上重复使用。不过,在Java 8中新增了一个@Repeatable注解,通过它可以实现重复注解。使用方法如下:(1)创建一个需要实现重复注解功能的注解:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface User { String value(); }
(2)创建一个注解容器:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Users { User[] value(); }
(3)为(1)中的注解加上@Repeatable并赋值,值为(2)中的注解容器的Class对象:
@Repeatable(Users.class)
使用示例:
@User("John") @User("Bob") public class SomeClass { }
相关文章推荐
- java注解Annotation的使用实例
- Java自定义注解Annotation的使用
- java 中JUint4 中使用注解(annotation)
- java注解应用实例 - Annotation, 自定义注解, 注解类规则
- java中注解的使用与实例 (二)
- Java基础-学习使用Annotation注解对象
- JUnit 4 使用 Java 5 中的注解(annotation)
- JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用简介,线程邮件发送实例
- Java基础笔记 – Annotation注解的介绍和使用 自定义注解
- Java Annotation 注解的介绍和使用 自定义注解
- paip.Java Annotation注解的作用and 使用
- Java基础-学习使用Annotation注解对象
- java注解应用实例 - Annotation, 自定义注解, 注解类规则
- java中注解的使用与实例
- Java基础-学习使用Annotation注解对象
- java注解应用实例 - Annotation, 自定义注解, 注解类规则
- Java Annotation实例:使用Annontaion简化开发
- Java基础复习笔记12Java自定义注解Annotation的使用
- 【Java】注解 annotation的介绍与使用
- Struts2零配置开发(注解Annotation的使用)一的简介与内容