注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记, 注解的作用: 1.给程序带入参数 2.取代一些属性文件文件 标记可以加在包、类,属性、方法,方法的参数以及局部变量上定义。
注解的应用场景
1. 重写父类方法时。 2. 运行一个无main的方法时,带有@Test的成员方法可以直接运行,无需main方法。 3. 框架中的配置。 现在框架都是基于注解的配置,简单方便。
常见注解
JDK中内置了很多注解,以下例举一些: 1. :用来标识作者名。 2. :用于标识对象的版本号,适用范围:文件、类、方法。 3. :用来修饰方法声明,告诉编译器该方法是重写父类中的方法, 如果父类不存在该方法,则编译失败
自定义注解
//定义格式 public @interface 注解名{ 属性类型 属性名(); } 如:定义一个名为Book的注解 public @interface Book{ String value(); }
注解的属性
1. 属性的作用 可以让用户在使用注解时传递参数,让注解的功能更加强大。 2. 属性的格式 格式1:数据类型 属性名(); 格式2:数据类型 属性名() default 默认值; 3. 属性定义示例 public @interface Student { String value(); int age() default 44; }
自定义注解的属性类型:
1.八种基本数据类型(int,float,boolean,byte,double,char,long,short) 2.String 类型 3.枚举类型 4.注解类型 5.以上数据类型的一维数组类型
使用自定义注解
//使用注意事项 如果属性有默认值,则使用注解的时候,这个属性可以不用赋值。 如果属性有默认值,则使用注解的时候,这个属性如果赋值了,以赋值为准。 如果属性没有默认值,那么在使用注解时一定要给属性赋值。 默认情况下,一个注解是可以用在一个类的任意地方的。 比如说: 类上、方法上、形参上 . 如果需要限定注解的使用范围,那么需要使用上这个注解。 默认情况下注解的内容也只是保留到了源码上,一旦经过编译之后,注解的内容就消失了,并没有保留到class文件上。想要获取到注解的数据就必须要Runtime这种保留策略.
一个特殊的注解属性value
1. 当注解中只有一个属性且名称是value,在使用注解时给value属性赋值可以直接给 属性值,无论value是单值元素还是数组类型。 2. 如果注解中除了value属性还有其他属性,且至少有一个属性没有默认值,则在使 用注解给属性赋值时,value属性名不能省略
元注解
用来定义注解的注解。
元注解的作用
用来说明自定义注解的在类中出现的位置和生命周期。
常用元注解
作用:定义该注解用在哪个位置,如果不写,默认是类中任何地方都可以使用。 可选的参数值在枚举类ElemenetType中包括: TYPE: 用在类,接口上 FIELD:用在成员变量上 METHOD: 用在方法上 PARAMETER:用在参数上 CONSTRUCTOR:用在构造方法上 LOCAL_VARIABLE:用在局部变量上 作用:定义该注解的生命周期。 可选的参数值在枚举类型RetentionPolicy中包括: SOURCE:注解只存在于Java源代码中,编译生成的字节码文件中就不存在了。 CLASS:注解存在于Java源代码、编译以后的字节码文件中,运行的时候内存中没有,这个是默认值。 RUNTIME:注解存在于Java源代码中、编译以后的字节码文件中、运行时内存中,程序可以通过反射获取该注解。
value=RetentionPolicy.RUNTIME)//定义注解生命周期。 (value={ElementType.FIELD,ElementType.METHOD})//定义注解使用位置。 (public @interface Book { int id() default 88;//图书编号 String author() default "小飞";//作者 double price() default 88.88;//单价 }
解析注解
通过Java技术,在程序运行时,获取注解中所有属性的过程则称为解析注解。
与注解解析相关的接口
Anontation:所有注解类型的公共接口,类似所有类的父类是Object。 AnnotatedElement:定义了与注解解析相关的方法, 常用方法以下四个: 1. boolean isAnnotationPresent(Class annotationClass); 判断当前对象是否有指定的注解,有则返回true,否则返回false。 2. T getAnnotation(Class<T> annotationClass); 获得当前对象上指定的注解对象。 3. Annotation[] getAnnotations(); 获得当前对象及其从父类上继承的所有的注解对象。 4. Annotation[] getDeclaredAnnotations(); 获得当前对象上所有的注解对象,不包括父类的。
获取注解对象
注解作用在那个成员上,就通过反射获得该成员的对象来得到它的注解。 //获取类的Class对象 Class clazz = 类名.class; //获取private的id属性对象 Field f = clazz.getDeclaredField("id"); f.setAccessible(true); //获取属性对象上是否有注解 flag= f.isAnnotationPresent(Book.class); //从属性对象上获取注解Book book = f.getAnnotation(Book.class); 如注解作用在方法上,就通过方法(Method)对象得到它的注解 //获取类的Class对象 Class clazz = 类名.class; // 得到方法对象 Method method = clazz.getDeclaredMethod("方法名",参数类型.class); // 根据注解名得到方法上的注解对象 Book book = method.getAnnotation(Book.class); 如注解作用在类上,就通过Class对象得到它的注解 // 获得Class对象 Class c = 类名.class; // 根据注解的Class获得使用在类上的注解对象 Book book = c.getAnnotation(Book.class);
获取注解对象上的数据
注解对象.注解属性名(); book.price();
使用反射获取注解的数据
value = RetentionPolicy.RUNTIME) (value = {ElementType.METHOD,ElementType.FIELD}) (@interface Book { String value() default "book"; String author() default "小芸";//作者 double price() default 11.11;//单价 } public class Demo { value="book001",author="李白",price=88) ( private int id; value="book002",author="诗仙",price=88) ( public void buy() throws Exception{ //获取Test09的Class对象 Class clazz = Demo.class; //获取public的buy方法 Method m = clazz.getMethod("buy"); //获取方法上是否有注解 boolean flag = m.isAnnotationPresent(Book.class); //从方法上获取注解Book Book book = m.getAnnotation(Book.class); //获取注解Book中的所有属性了 System.out.println(book.value()); System.out.println(book.author()); System.out.println(book.price()); //上述代码叫反射注解 //获取private的id属性对象 Field f = clazz.getDeclaredField("id"); f.setAccessible(true); //获取属性对象上是否有注解 flag= f.isAnnotationPresent(Book.class); //从属性对象上获取注解Book book = f.getAnnotation(Book.class); //获取注解Book中的所有属性了 System.out.println(book.value()); System.out.println(book.author()); System.out.println(book.price()); } public static void main(String[] args) throws Exception { Demo test = new Demo(); test.buy(); } }
模拟JUnit测试的@Test注释
public class Demo { public static void main(String[] args) throws Exception{ //获取MyTestDemo类的Class对象 Class clazz = MyTestDemo.class; //创建对象 Object obj = clazz.newInstance(); //获取public的方法 Method[] ms = clazz.getMethods(); //迭代 for (Method m : ms) { //判断是否在运行时,看到方法上的注解@MyTest boolean flag = m.isAnnotationPresent(MyTest.class); //如果在运行时可见 if(flag){ //执行这个方法 m.invoke(obj,null); } } } }
/** * @MyTest注解 */ value= RetentionPolicy.RUNTIME) (value= ElementType.METHOD) (public @interface MyTest { } /** * 使用MyTest注解 */ public class MyTestDemo { public void test1(){ System.out.println("public void test1()"); } public void test2(){ System.out.println("public void test2()"); } public void test3(){ System.out.println("public void test3()"); } }