一、基础
-
作用
- 通过反射技术去得到类中的注解,以决定如何运行类
-
定义
-
注解(注释类型)是一种引用数据类型,是代码中的特殊标记,用于替代配置文件,决定类如何运行,编译之后生成xxx.class文件
-
自定义注解语法格式:[修饰符列表] @interface 注解类型名{ }
-
public @interface AnnotationTest {}
-
-
用法
- 注解使用时语法格式为:@注解类型名
- 注解可以出现在类上、方法上、变量上(前)、注解类上、接口上等几乎任何地方
-
元注解(meta-annotation):用于对注解类型进行注解的注解类,常用元注解如下
- @Target:描述注解的使用范围(位置)
- @Target(ElementType.METHOD)只对方法生效
- @Retention:描述注解保留的范围(最终保存在哪个阶段)
- @Retention(RentationPolicy.SOURCE)表示该注解只被保留在java源文件中
- @Retention(RentationPolicy.CLASS) 表示该注解被保存在class文件中
- @Retention(RentationPolicy.RUNTIME)表示该注解被保存在class文件中,并且可以被反射机制所读取到
- @Documented:描述使用javadoc工具类生成帮助文档时是否需要保留其注解信息
- @Inherit:使被它修饰的注解具有继承性(若一个类使用@Inherit修饰的注解,其子类自动具有该注解)
- @Target:描述注解的使用范围(位置)
二、JDK内置注解
- @Deprecated 表明此方法已废弃、暂时可用,但后续不会更新、可能删除,建议不要调用此方法,有更好的解决方案
- 元注解:@Documented、@Retention(RetentionPolicy.RUNTIME)、@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
- 该注解保存在字节码文件中,并且可以被反射机制所读取
- 该注解可放置在Retention所标识之处
- @Override 重写方法
- 元注解:@Target(ElementType.METHOD)、@Retention(RetentionPolicy.SOURCE)
- 这个注解只能注解方法
- 标识性注解,编译阶段供编译器参考,与运行阶段无关
- 带有该注解的方法,编译器会进行编译检查是否为重写父类方法,否则报错
- @SuppressWarnings 告诉编译器忽略指定(未指定泛型、未使用和过时)的警告,不用在编译完成后出现警告信息
三、注解中的属性
- 注解当中可以定义属性,看似方法,称为属性
- 若属性有指定默认值,则使用注解时括号中可以不写该值
//自定义注解代码
public @interface AnnotationTest {
String name();
String color();
//属性指定默认值
int age() default 25;
String[] email();
}
//测试方法中的代码
public class Test {
//@AnnotationTest(属性名=属性值)
//指定了默认属性后,括号中可以不写
@AnnotationTest(name="abc",color="red",email={"abc@qq.com","abc@163.com"})//若数组中只有一个元素,则可省略大括号
public void test(){
}
}
- 若注解定义中属性只有一个且属性名为value,则在注解使用时,可以不写属性名和"=",直接填写属性值
//自定义注解代码
public @interface AnnotationTest {
String value();
}
//测试方法中的代码
public class Test {
@AnnotationTest("hello")
public void test(){
}
}
- 属性类型:byte、short、int、long、float、double、boolean、char、String、Class、枚举类型以及前面每一种的数组形式
四、反射注解
//自定义注解
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationTest {
String value() default "abc";
}
//使用了该自定义注解的类
@AnnotationTest("hello")
public class User {
public int no;
}
//测试类
//若AnnotationTest处于不同包下,则需要导包
public static void main(String[] args) throws Exception{
//获取使用了注解的那个类,若是类中的方法或属性用了注解,则还需要进一步取得类中的方法或属性
Class c = Class.forName("bean.User");
//判断类上面是否有@AnnotationTest
if ( c.isAnnotationPresent(AnnotationTest.class)) { //true
//获取该注解对象,此处使用了强制类型转换
AnnotationTest annotationTest = (AnnotationTest)c.getAnnotation(AnnotationTest.class);
//获取注解对象的属性,与调用接口相似
String value = annotationTest.value();
}
}
五、注解审查例子
- 注解在程序当中等同于一种标记,一种自己设定规则。
- 需求:被@Id注解的类中必须含有int类型的id属性
//自定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Id {
}
//自定义实体类
@Id()
public class User {
public int id;
}
//自定义异常
public class HasNotIdPropertyException extends RuntimeException{
//需要写两个构造方法,抛出异常信息
public HasNotIdPropertyException() {
}
public HasNotIdPropertyException(String message) {
super(message);
}
}
//测试类
//测试方法中的代码
public class Test {
public static void main(String[] args) throws Exception{
//获取使用了注解的那个类
Class c = Class.forName("bean.User");
//判断类上是否存在Id注解
if(c.isAnnotationPresent(Id.class)){
//当一个类上面有@Id注解的时候,要求类中必须存在int类型的id属性
//如果没有int类型的id属性则报异常
Field[] fields = c.getDeclaredFields();
//给一个默认的标记
boolean isOk=false;
for (Field field:fields) {
if("id".equals(field.getName()) && "int".equals(field.getType().getSimpleName())){
//表示这个类是合法的类,有@Id注解,则这个类中必须有int类型的id
isOk=true;//表示合法
break;
}
}
//判断是否合法
if(!isOk){
throw new HasNotIdPropertyException("被@Id注解标注的类中必须有一个int类型的id属性");
}
}
}
}