注解的作用
注解(Annotation) 为我们在代码中添加信息提供了一种形式化的方法,是我们可以可以稍后 某个时刻方便使用这些数据(通过解析注解来使用这些数据),
常见的作用有以下几种:
(1)生成文档,这是最常见的,也是Java最早的注解。常用的有@see@parm@return等
(2)在编译时进行编译检查。如@Override放在方法前,如果你这个方法并不是覆盖了父类的方法,则编译时就能检查出来
(3)跟踪代码依赖性,实现替代配置文件功能。比较常见的是spring2.5开始基于注解配置。作用就是减少配置。现在的框架基本都是使用这配置来减少配置文件的数量
JDK自带的注解
@Override表示当前方法覆盖了父类的方法
次注释只适用于修饰方法,表示一个方法声明打算重写超类中的另一个方法声明。如果方法利用此注释类型进行注解但没有重写
超类方法则编译器生成一条次错误消息
@Deprecated表示方法已经过时,方法上有横线,使用时会有警告
通常它很危险或存在更好的选择,在使用不赞成的程序元素或在不被赞成的代码中执行重写时,编译器会发出警告
开发自定义注解
自定义注解的语法规则
(1)使用@interface关键字定义注解,注意关键字的位置使用@interface自定义注解时自动继承了java.lang.annotation.Annotation接口
由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口
(2)成员以无参无异常的方式声明,注意区别一般类成员变量的声明其中的每一个方法实际上声明了一个配置参数,方法的名称就是参数的名称
(3)可以使用default关键字为成员指定一个默认值
(4)成员的类型是受限的,合法的类型包括原始类型以及String 、Class、Annotation、Enumeration(Java的基本数据类型有8种byte(字节)、
short(短整型)、int(整数型)、long(长整型)、float(单精度浮点数类型)、double(双精度浮点数类型)、char(字符类型)、boolean(布尔类型))
(5)注解类可以没有成员,没有成员的注解称为标识注解,例如JDK注解中的@Override、@Deprecation
(6)如果注解只有一个成员,并且把成员取名为value(),则在使用时可以忽略成员名和赋值号“=”,例如
jdk注解的@SuppviseWarnings;如果成员名 不为value,则使用时需要指明成员名和赋值号“=”
实例代码中,定义一个字定义注解
在其他类中使用这个注解
在使用注解的时候必须要指明两个参数,多个参数之间通过逗号分隔
声明注解成员的时候可以通过default为成员提供缺省值
如果注解只有一个成员,并且把成员取名为value(),则在使用时可以忽略成员名和赋值号“=”
元注解
何为元注解?就是注解的注解,就是给你自己定义的注解添加注解,你自己定义了一个注解,但是你想要你的注解有什么样的
功能,此时就需要用元注解对你注解进行说明了。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其他
annotation类型作说明
1、@Target
@Target 说明了Annotation所修饰的对象范围:即注解的作用域,用于说明注解的使用范围(即注解可以用在什么地方,比如类
的注解,方法的注解,成员变量的注解等等)
注意:如果Target元注解没有出现,那么定义的注解可以应用于程序任何元素
取值是在java.lang.annotation.ElementType这个枚举中规定的:
1、Constructor:用于描述构造器
2、FIELD:用于描述域
3、LOCAL_VARIABLE:用于描述局部变量
4、METHOD:用于描述方法
5、PACKAGE:用于描述包
6、PARAMETER:用于描述参数
7、TYPE:用于描述类、接口(包括注解类型)或enum声明
实例代码
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface MyAnnotation {
//成员以无参数无异常的的方式声明,注意区别一般类成员变量的声明
//String classname();
//注解中可以添加多个成员,也可以是基本类型
int value() default 100;
}
2、@Retention
@Retention定义了该Annotation被保留的时间长短;
(1)某些Annotation定义了该Annotation仅出现在源代码中,而被编译器丢弃;
(2)而另一些却被编译在class文件中,编译在class文件中的Annotation可能被虚拟机忽略
(3)而另一些在class中被装载时被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)
使用这个meta-Annotation可以对Annotation的生命周期限制
@Retention的取值是在RetentionPoicy这个枚举中规定的
1、SOURCE:在源文件中有效(即源文件保留)
2、CLASS:在class文件中有效(即class保留)
3、RUNTIME:在运行时有效
实例代码
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
//成员以无参数无异常的的方式声明,注意区别一般类成员变量的声明
//String classname();
//注解中可以添加多个成员,也可以是基本类型
int value() default 100;
}
注意:注解的RetentionPolicy的属性值时RUNTIME,这样注解的处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时的逻辑处理
3、@Documented
@Ducumented用于描述其他类型的annotation应该被作为被标注的程序员的公共的API,因此可以被例如javadoc此类的工具文档化。Documented
是一个标记注解,没有成员
4、@Inherited
@Inherited元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,
则这个annotation将被用于该class的子类
注意@Inherited annotation 类型是被标注过的class的子类所继承。类并不从它实现的接口继承annotation,方法并不从它所重载的方法继承annotation
当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去
查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现。或者达到类
继承结构的顶层
定义注解一
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
//这个注解可以被子类继承
@Inherited
public @interface Dbinfo {
String tableName() default "emp";
}
定义注解二
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Dbinfo2 {
String tableName() default "dept";
}
定义一个父类
@Dbinfo(tableName = "emp")
public class SupClas {
}
定义一个子类
package com.demo.annotation;
@Dbinfo2(tableName = "dept")
public class SubClass extends SupClas {
}
通过反射获取子类使用的所有的注解
package com.demo.annotation;
import java.lang.annotation.Annotation;
public class ReflectTest {
public static void main(String[] args) {
getTypeAnnotation();
}
//获取子类的注解
public static void getTypeAnnotation(){
//获取的子类的注解
Class clazz=SubClass.class;
//获取Class上面使用的多个注解,获取的是Annotation[]
//一个程序元素上可以使用多个注解
Annotation[] annotations= clazz.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
}
}
Connected to the target VM, address: '127.0.0.1:51907', transport: 'socket'
Disconnected from the target VM, address: '127.0.0.1:51907', transport: 'socket'
@com.demo.annotation.Dbinfo(tableName=emp)
@com.demo.annotation.Dbinfo2(tableName=dept)
Process finished with exit code 0
注解开发实例:通过注解创建数据库表
1、创建Column注解,表示数据库的字段信息
package com.demo.annoDemo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD}) //注解只能用在属性上
@Retention(RetentionPolicy.RUNTIME) //运行时保留
public @interface Column {
//数据库的相关属性:字段名称、字段类型、字段长度、约束
/**
* 字段名称必须声明
* @return
*/
String columnName();
/**
* 字段类型必须声明
* @return
*/
String dataType();
/**
* 长度缺省值为16
* @return
*/
int dataLength() default 16;
/**
* 字段的约束默认是“”
* @return
*/
String constraint() default "";
}
2.创建Table注解,表示数据库中的表
package com.demo.annoDemo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
/**
* 必须指定表名
* @return
*/
String tableName();
}
3.创建JavaBen类,使用定义的注解
package com.demo.annoDemo;
import java.io.Serializable;
/**
* 创建Javabean;Javabean的规范
* 1、类中声明私有属性,并且提供公开的get/set方法
* 2、实现serializable接口,并且在类中声明serialVersionUID
* 3、覆盖hashcode()/equals()方法
* 4、覆盖toString()方法
* 5、声明类中的无参构造方法
*/
@Table(tableName = "tab_user")
public class User implements Serializable {
@Column(columnName = "id",dataLength = 6,dataType = "int",constraint = "primary key")
private int id;//对应表中的主键字段
@Column(columnName = "name",dataType = "varchar",dataLength = 16,constraint = "unique")
private String name;//name字段
@Column(columnName = "passWord",dataType = "varchar",dataLength = 16,constraint = "unique")
private String passWord;
}
4.创建Main方法,读取JavaBen类中的注解信息,根据注解信息自动生成DDL语句
package com.demo.annoDemo;
import java.lang.reflect.Field;
public class AutoCreateTable {
public static void main(String[] args) {
buildSql();
}
/**
* 自动构造SQL语句
*/
public static void buildSql(){
StringBuffer str =new StringBuffer("CREATE TABLE ");
//获取user对应的class
Class clazz=User.class;
//获取user类中使用的table注解
Table table= (Table) clazz.getAnnotation(Table.class);
str.append(table.tableName()+"(");
//获取user类中声明的属性
Field[] fields=clazz.getDeclaredFields();
//遍历属性的数组
for (Field field : fields) {
if (field.isAnnotationPresent(Column.class)) {
Column column= field.getAnnotation(Column.class);
//获取字段名
String columnName=column.columnName();
str.append(columnName+" ");
//获取字段类型
String dataType = column.dataType();
str.append(dataType+" ");
//获取字段长度
int dataLength=column.dataLength();
str.append("(");
str.append(dataLength);
str.append(") ");
//获取约束
String constraint=column.constraint();
str.append(constraint+" "+",");
}
}
System.out.println(str.substring(1,str.length()-1)+")");
}
}