注解概述
1.注解概述
注解是JDK1.5的新特性。注解的使用,相当于为程序打上某种标记。
注解(annotation),是一种代码级别的说明,和类 接口平级关系.
javac编译器、开发工具和其他程序可以通过反射,来了解你的类及各种元素上有注解标记,根据不同的注解标记,完成对应的功能。
注解可以加在包、类,属性、方法,方法的参数以及局部变量上定义
2.应用场景
1.生产帮助文档 例如@author和@version 2.执行编译期的检查 例如:@Override
3.框架的配置(替代配置文件)
3.常见注解
1.@author:用来标识对象的作者名。
2. @version:用于标识对象的版本号。
3.@Override:描述方法的重写。
4. @Deprecated:标记过时。
5.@SuppressWarnings:压制警告。
6.@FunctionalInterface:函数式接口
7. @Test:测试注解
自定义注解
1.格式
注解本质上就是一个接口,注解中的方法叫注解属性
2.注解属性定义
案例
// 无属性的注解 public @interface MyAnnotation1 { }
// 带属性的注解: 属性类型 属性名(); public @interface MyAnnotation2 { // 1.基本类型 byte a(); short b(); int c(); long d(); float e(); double f(); char g(); boolean h(); // 2.String类型 String str(); // 3.枚举类型 Gender gender(); // 4.注解类型 MyAnnotation1 ma1(); Override o(); // 5.Class类型 Class cls(); // 6.以上类型的一维数组类型 int[] arr1(); String[] arr2(); Gender[] arr3(); MyAnnotation1[] arr4(); Class[] arr5(); }
使用注解----重点
4.注解属性赋值
如果一个注解没有属性,那么就不需要给注解属性赋值,直接使用即可 @注解名
如果一个注解中有属性,那么使用注解的时候一定要给注解所有属性赋值 @注解名(属性名=值,属性名2=值2,...)
5.注意事项:
一旦注解有属性了,使用注解的时候,属性必须有值
若属性类型是一维数组的时候,当数组的值只有一个的时候可以省略{}
如果注解中只有一个属性,并且属性名为value,那么使用注解给注解属性赋值的时候,注解属性 名value可以省略
注解属性可以有默认值 格式:属性类型 属性名() default 默认值;
6.需求:演示自定义注解的定义及使用
// 没有属性的注解 public @interface MyAnnotation1 { }
// 有属性的注解 public @interface MyAnnotation2 { // 属性 String name(); int age(); }
@MyAnnotation1 @MyAnnotation2(name = "itheima", age = 18) public class Test { @MyAnnotation1 @MyAnnotation2(name = "itheima", age = 18) int num; @MyAnnotation1 @MyAnnotation2(name = "itheima", age = 18) public static void main(String[] args) { /* 注解属性赋值 如果一个注解没有属性,那么就不需要给注解属性赋值,直接使用即可 @注解名 如果一个注解中有属性,那么使用注解的时候一定要给注解所有属性赋值 @注解名(属性名=值,属性名2=值2,...) 注解可以使用在类\接口...上,成员变量上,成员方法上,形参上,构造方法上,局部变量上 */ @MyAnnotation1 @MyAnnotation2(name = "itheima", age = 18) int a = 10; } }
使用的注解的注意事项
public @interface MyAnnotation1 { String name(); int age(); }
public @interface MyAnnotation2 { String[] arr(); }
public @interface MyAnnotation3 { String value(); }
public @interface MyAnnotation4 { String name() default "java"; int age() default 18; }
public @interface MyAnnotation23 { String[] value(); }
public @interface MyAnnotation44 { String name() default "java"; int age() ; }
public class Test { public static void main(String[] args) { /* 注意事项: 一旦注解有属性了,使用注解的时候,属性必须有值 若属性类型是一维数组的时候,当数组的值只有一个的时候可以省略{} 如果注解中只有一个属性,并且属性名为value,那么使用注解给注解属性赋值的时候,注解属性名value可以省略 注解属性可以有默认值 格式:属性类型 属性名() default 默认值; */ } //一旦注解有属性了,使用注解的时候,属性必须有值 @MyAnnotation1(name="java",age = 20) public void method1(){} //若属性类型是一维数组的时候,当数组的值只有一个的时候可以省略{} //@MyAnnotation2(arr = {"itheima","itcast"}) //@MyAnnotation2(arr = {"itheima"}) //@MyAnnotation2(arr = "itheima") public void method2(){} // 如果注解中只有一个属性,并且属性名为value,那么使用注解给注解属性赋值的时候,注解属性名value可以省略 //@MyAnnotation3(value = "java") @MyAnnotation3("java") public void method3(){} //若属性类型是一维数组的时候,当数组的值只有一个的时候可以省略{} //如果注解中只有一个属性,并且属性名为value,那么使用注解给注解属性赋值的时候,注解属性名value可以省略 //@MyAnnotation23(value = {"java"}) //@MyAnnotation23(value = "java") @MyAnnotation23("java") public void method23(){} //注解属性可以有默认值 格式:属性类型 属性名() default 默认值; @MyAnnotation4 // 所有属性都有默认值,使用的时候可以不给属性赋值,使用的就是默认值 public void method4(){} @MyAnnotation44(age = 18) // 部分属性有默认值,那这部分属性可以不赋值,使用默认值,其余没有默认值的属性必须都赋值 public void method44(){} }
元注解
1.概述
元注解是定义在注解上的注解,可以理解为由java定义好的,具有特殊意义的注解。
2.常见元注解
-@Retention:标记该注解保留到哪个操作阶段.
-@Target:标记该注解作用在什么位置
// 标记该注解可以使用在哪个位置 @Target(value = {ElementType.TYPE,ElementType.FIELD,ElementType.CONSTRUCTOR,ElementType.METHOD,ElementType.PARAMETER,ElementType.LOCAL_VARIABLE}) //@Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation1 { }
@MyAnnotation1 public class Test1 { @MyAnnotation1 int num; @MyAnnotation1 public Test1(int num) { this.num = num; } @MyAnnotation1 public static void main(@MyAnnotation1 String[] args) { /* 元注解: 定义注解的注解 常见的元注解: @Target:标记该注解作用在什么位置,注解不加@Target元注解修饰,默认是任意位置都可以使用 @Target元注解中只有一个属性: ElementType[] value() 该属性的类型是ElementType数组类型,而ElementType是枚举类型 ElementType枚举类型的枚举值: TYPE--------类型 FIELD-------成员变量 METHOD------成员方法 PARAMETER---参数 CONSTRUCTOR---构造方法 LOCAL_VARIABLE---局部变量 ... @Retention:标记该注解保留到哪个操作阶段.默认是源码阶段 @Retention元注解只有一个属性: RetentionPolicy value(); RetentionPolicy是枚举类型: SOURCE---->源码阶段 CLASS----->字节码阶段 RUNTIME--->运行阶段 */ @MyAnnotation1 int a = 10; } }
注解解析
1.概述
-使用Java技术获得注解上数据的过程则称为注解解析。
-Annotation是描述字节码文件中注解的类,通过对Annotation对象类型判断,可以对”被注解元素”做相应操作。
2.解析原理
-1.java.lang.reflect.AnnotatedElement代表当前JVM中的“被注解元素”。
-2.Class、Method、Field、Constructor等实现了AnnotatedElement接口,都是可被注解的"元素"。
-3.(通过Java的反射机制)使用这个接口中声明的方法可以读取“被注解元素”的注解.
3.相关方法
T getAnnotation(Class<T> annotationType):得到指定类型的注解对象。没有返回null。 boolean isAnnotationPresent(Class<?extends Annotation> annotationType):判断指定的 注解有没有。
案例
@Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String name(); int age(); }
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation1 { }
@MyAnnotation(name = "java", age = 18) @MyAnnotation1 public class Test { public static void main(String[] args) throws Exception{ /* 注解解析: 指的是获取注解或者获取注解属性值的过程 被注解的元素: 被注解修饰的元素都是被注解元素,例如: Class,Method,Field,Constructor等等 解析注解的步骤: 1.获取被注解的元素 2.通过被注解的元素获取其上面的注解 3.通过注解获取注解上的属性值 使用的API: 被注解元素接口AnnotatedElement的api---->所有被注解的元素都实现了该接口,也就意味着拥有这些方法 <T extends Annotation> T getAnnotation(Class<T> annotationClass) 如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。 Annotation[] getAnnotations() 返回此元素上存在的所有注释。 Annotation[] getDeclaredAnnotations() 返回直接存在于此元素上的所有注释。 boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果指定类型的注释存在于此元素上,则返回 true,否则返回 false。 */ // 1.获取被注解的元素 Class<Test> c = Test.class; // 2.通过被注解的元素获取其上面的注解 MyAnnotation annotation = c.getAnnotation(MyAnnotation.class); MyAnnotation1 annotation1 = c.getAnnotation(MyAnnotation1.class); // 3.通过注解获取注解上的属性值 System.out.println("annotation.name() = " + annotation.name()); System.out.println("annotation.age() = " + annotation.age()); // 4.获取所有的注解 Annotation[] annotations = c.getAnnotations(); for (Annotation an : annotations) { System.out.println("an = " + an); } // 5.判断Test类上是否有指定的注解 boolean res1 = c.isAnnotationPresent(MyAnnotation.class); boolean res2 = c.isAnnotationPresent(MyAnnotation1.class); boolean res3 = c.isAnnotationPresent(Override.class); System.out.println("res1 = " + res1);// true System.out.println("res2 = " + res2);// true System.out.println("res3 = " + res3);// false System.out.println("--------------"); // 1.获取被注解的元素---method方法 Method m = c.getDeclaredMethod("method"); // 2.获取该方法上的MyAnnotation的注解 MyAnnotation annotation2 = m.getAnnotation(MyAnnotation.class); // 3.通过注解获取注解的属性值 System.out.println(annotation2.name() + "," + annotation2.age()); // 4.判断method方法上是否有MyAnnotation注解 boolean res = m.isAnnotationPresent(MyAnnotation.class); boolean result = m.isAnnotationPresent(MyAnnotation1.class); System.out.println("res = " + res);// true System.out.println("result = " + result);//false } @MyAnnotation(name = "itheima", age = 1000) public static void method() { System.out.println("method"); } }
模拟Test注解
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MyTest { }
public class TestDemo { @MyTest public void test1(){ System.out.println("test1..."); } @MyTest public void test2(){ System.out.println("test2..."); } public void test3(){ System.out.println("test3..."); } }
public class MyTestRun { public static void main(String[] args) throws Exception { // 模拟Test注解,点击方法名运行的解析代码 /*// 1.获取TestDemo类的字节码对象 Class<TestDemo> c = TestDemo.class; // 2.获取被注解的元素(test1方法) Method m = c.getMethod("test1"); // 3.执行该方法 TestDemo td = c.getDeclaredConstructor().newInstance(); m.invoke(td);*/ //System.out.println("----------------------"); // 模拟Test注解,点击类名运行的解析代码 // 1.获取TestDemo类的字节码对象 Class<TestDemo> c = TestDemo.class; // 2.获取类中所有的成员方法 Method[] methods = c.getMethods(); // 3.循环遍历所有的方法 for (Method m : methods) { // 4.判断该方法上是否有MyTest注解 boolean res = m.isAnnotationPresent(MyTest.class); // 5.如果有,就执行该方法 if (res){ m.invoke(c.getDeclaredConstructor().newInstance()); } } } }