您的位置:首页 > 编程语言 > Java开发

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 注解