Java高级之注解
初识注解
概念入门
-
在现实世界中,我们可以用沉鱼、落雁、闭月、羞花来形容女子容貌美丽;在动物世界中,我们可以用凶猛、强悍、睿智、威武来形容狮子老虎的地位;那么联系到在Java世界,我们应该用何来形容Java源码的类、方法、字段以及参数呢?答案是注解。
-
注解:
Annotation
,翻译过来便是:注释、释文。它可以为Java
源码的类、方法、字段以及参数提供一些描述性的信息。 -
元数据:我们将类、方法、字段以及参数当作是
Java
世界的数据,注解用来描述这些数据,那么,我们可以将注解理解为元数据Metadata
,就是描述数据的数据。 -
元注解:既然存在描述数据的数据,那么也应该存在描述注解的注解,例如我们常见的
@Target
、@Retention
等,它是用来修饰注解的注解。
简单理解
-
我们可以将注解理解为代码中的特殊标记,这些标记并不会对代码的逻辑产生任何影响;这些对数据的描述可以在编译、加载以及运行时被读取,而如何使用这些注解,由各类工具比如代码分析工具、开发工具和部署工具等决定。
-
举个不恰当的例子:如下,当子类重写了父类的
hashCode()
方法时,在重写的hashCode()
方法上便会存在@Override
注解,该注解并不会对代码的具体逻辑产生影响,但编译器在编译时,会检查该方法是否正确地实现了重写。也就是说,当编译器发现hashCode()
方法上存在@Override
,编译器会认为它重写了父类的方法,那么编译器就会去检查该方法的返回值、方法的参数等是否与父类的一致,以决定是否让该类编译通过。@Override public int hashCode() { return hash; }
-
也就是说,通过使用注解,可以在不改变原有逻辑的情况下, 在源文件中嵌入一些补充信息。这些补充的信息可以被相应的工具识别。
-
现在有这样的一种理解:在某种程度上,框架 = 注解 + 反射 + 设计模式。
注解的使用
- java修饰符大家一定不陌生
- 访问修饰符public、protected、default 、private可以保护对类、变量、方法和构造方法的访问。
- 非访问修饰符static、final、abstract、synchronized、volatile等可以用来修饰类、方法、变量以及线程等,用以实现额外的一些需求。
- 使用注解的方式很简单:
- 在该注解前面添加@符号;
- 将该注解当成一个修饰符来修饰它所支持的程序元素。
自定义注解
常见的注解
- 编译时进行格式检查的注解
- @Override:限定重写父类方法;
- @Deprecated:表示所修饰的元素(类, 方法等)已过时;
- @SuppressWarnings:抑制编译器警告。
- 元注解:
- @Target:用于指定被修饰的注解可以用于修饰哪些程序元素(类或接口、字段、方法、构造方法、方法参数);
- @Retention:定义了注解的生命周期:SOURCE、CLASS(默认)、RUNTIME。如果想要通过反射获取注解,则必须将生命周期声明为RUNTIME;
- @Repeatable:注解是否可重复;
- @Inherited:注解是否可继承。
自定义注解流程
-
自定义一个注解,该注解名字为“MyExecute”,在运行时有效,作用范围在方法上;其中有两个成员变量:value与ignore。代码如下:
/** * 指定该注解的生命周期 * 1、仅编译期:RetentionPolicy.SOURCE; * 2、仅class文件:RetentionPolicy.CLASS; * 3、运行期:RetentionPolicy.RUNTIME。 * 只有在运行期才可以通过反射获取到 */ @Retention(RetentionPolicy.RUNTIME) /** * 指定该注解能够用于哪些程序元素 * 1、类或接口:ElementType.TYPE; * 2、字段:ElementType.FIELD; * 3、方法:ElementType.METHOD; * 4、构造方法:ElementType.CONSTRUCTOR; * 5、方法参数:ElementType.PARAMETER。 */ @Target({ElementType.METHOD}) //创建注解使用 @interface 关键字来声明 public @interface MyExecute { /** * 1、成员变量无参数方法的形式来声明:方法名是该成员的名字、返回值是该成员的类型 * 2、可以指定初始值,初始值用 default 关键字 * 3、如果只要一个参数成员,建议使用参数名为value() * @return */ String value() default "MyAnnotation"; /** * 定义成员变量ignore() 类型为boolean 只有方法上该成员变量值为true时,该注解才起作用, * 需要反射来配合该注解。 */ boolean ignore() default false; }
-
自定义一个类,类中有两个方法,在这两个方法上使用该注解:
public class AnnotationTest { @MyExecute(value = "自定义的注解MyExecute,并且ignore为true!", ignore = true) public void ignoreTrue(){ System.out.println("ignore为真,不调用,用来测试自定义注解!"); } //不指定ignore的值,默认为false,会执行。 @MyExecute(value = "自定义的注解MyExecute,并且ignore为默认值false!") public void ignoreFalse(){ System.out.println("ignore为假,不忽视,调用方法,用来测试自定义注解!"); } }
-
测试自定义注解,自定义注解要有实际意义,需要配上注解的信息处理流程,也就是使用反射获取到注解并进行处理。
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException { //使用反射来使自定义注解有实际意义 AnnotationTest annotationTest = new AnnotationTest(); //1、获取当前类的Class实例 使用运行时类的.class属性来获取 //也可以使用annotationTest.getClass()方法来获取 Class clazz = AnnotationTest.class; //2、获取所有的方法,并拿到方法上的注解 Method[] methods1 =clazz.getDeclaredMethods(); for (Method method : methods1) { //判断这个方法是否存在自定义的注解 if(method.isAnnotationPresent(MyExecute.class)){ //拿到该注解下的成员ignore,并判断是否为false MyExecute myExecute = method.getAnnotation(MyExecute.class); System.out.println("***************************************"); System.out.println("该方法名为:"+method.getName()+",它上面注解的value成员值为:"+myExecute.value()); if(!myExecute.ignore()){ System.out.println("当ignore为false时执行该方法!!!"); method.invoke(annotationTest,null); } } } }
-
运行结果
-
注意
- 注解的成员如果没有默认值时,使用注解时,需要指定该成员的值,除非该成员有默认值;指定格式为:“参数名 = 参数值”;当只有一个成员且名称为value(),指定参数值时可以取消“value = ”;
- 自定义注解要想使其有意义,需要与注解的信息处理流程(反射)相配合。
原创不易,欢迎转载,转载时请注明出处,谢谢!
作者:潇~萧下
原文链接:https://www.cnblogs.com/manongxiao/p/13493759.html