Java学习之注解元数据

简介

注解也被称为元数据,它为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便地使用这些数据。

注解一定程度上是把元数据和源代码文件结合在一起,而不是保存在外部文档中这一大的趋势之下所催生的。

在Java中,有一些内置的注解,比如:

  • @Override:表示当前的方法顶柜将覆盖超类中的方法,如果拼写错误或者覆盖不符合标准,编译器就会发出错误提示。
  • @Deprecated:表示被修饰的元素已经被弃用,不建议程序员使用。
  • @SuppressWarnings:关闭不当的编译器警告信息
    除了这三个还有一些我们后面会提到


基本语法


注解定义

下面是一个注解的定义。可以看到,注解看起来很像接口的定义,事实上,与其他任何Java接口一样,注解也将会编译成class文件。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationDemo1 {
    
    
}

除了@符号以外,它很像一个空接口。

在定义注解时,会需要一些元注解,如@Target和@Retention。前者用来定义注解将应用于何处(类、方法还是域),后者定义该注解的可用级别(源代码,编译时,运行时)

在注解中,通常都会包含一些元素以表示某些之。当分析处理注解时,程序或工具可以利用这些值。注解中的元素看起来就像是接口的方法,唯一的区别就是我们可以指定其默认值。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationDemo1 {
    
    
    public int id();
    public String description() default "no description";
}

注意,id和description类似方法定义。如果在使用该注解时没有给出description值,则该注解的处理器会使用此元素的默认值。

下面是此注解的使用示例

class demo{
    
    
    @AnnotationDemo1(id=1)
    public void f1(){
    
    
        
    }
    
    @AnnotationDemo1(id=2,description = "f2")
    public void f2(){
    
    }
}

元注解

Java目前只内置了三种标准注解(一开始提到的三个注解)以及四种元注解。元注解专职负责注解其他注解:

注解 说明 参数
@Target 表示该注解可以用于什么地方 CONSTRUCUOR:构造器
FIELD:域
LOCAL_VARIABLE:局部变量
METHOD:方法声明
PACKAGE:包声明
PARAMETER:参数说明
TYPE:类、接口(包括注解)或enum
@Rentention 表示需要在什么级别保存该注解信息 SOURCE:注解将被编译器丢弃
CLASS:注解在class文件中可用。会被jvm丢弃
RUNTIME:JVM在运行期间保留注解,因此可以通过反射机制读取注解信息
@Documented 将此注解包含在javadoc中
@Inherited 允许子类继承父类中的注解

大多数时候,我们需要定义自己的注解,并编写自己的处理器去处理他们



注解处理器

如果没有又来读取注解的工具,那注解也不会比注释更有用。使用注解的过程中,很重要的一个部分就是创建与使用注解处理器。JavaSE5拓展了反射机制的API,以帮助我们构造这类工具。

下面例子是一个简单的注解处理器,我们将用它来读取被自定义注解修饰的demo类,并使用反射查找@AnnotationDemo1标记

public class AnnotationDemo2 {
    
    
    public static void main(String[] args) {
    
    
        AnnotationDemo1Tracker.handleAnnotation(demo.class);
    }
}

class AnnotationDemo1Tracker{
    
    
    public static void handleAnnotation(Class<?> cl){
    
    
        for (Method method:cl.getDeclaredMethods()){
    
    
            AnnotationDemo1 annotationDemo1 = method.getAnnotation(AnnotationDemo1.class);
            if(annotationDemo1 != null){
    
    
                System.out.println(method.getName()+"---id:"+annotationDemo1.id()+"---description:"+annotationDemo1.description());
            }
        }
    }
}

上面例子中,注解处理器使用到了两个反射的方法:getDeclaredMethods()和getAnnotation(),它们都属于AnnotatedElement接口(Class、Method与Field等类都实现了该接口)。getAnnotation()方法返回指定类型的注解对象,如果目标方法上没有该类型的注解,则返回null。然后我们通过调用id()和description()方法从返回的注解中提取元素的值。


注解元素

注解中可用的元素类型如下所示:

  • 所有基本类型
  • String
  • Class
  • enum
  • Annotation
  • 以上类型的数组
    如果我们使用了其他类型,那编译器就会报错。注意包装类型也不被允许。注解也可以作为元素的类型,也就是说注解可以嵌套。这是一个非常有用的特性。

默认值限制

编译器对元素的默认值有非常严格的要求。首先,元素不能有不确定的值。也就是说,元素必须要么具有默认值,要么在使用注解时提供元素的值。
其次,对于非基本类型的元素,不论是默认值还是声明时的值,都不能为null。这个约束使得处理器很难表现一个元素的存在或者缺失状态,为了绕开这个约束,我们只能自己定义一些特殊的值,比如空字符串或者负数,用来标识一个元素不存在。


生成外部文件

我们在使用hibernate、struts2以及早期的Spring框架时会发现我们除了编写代码以外,还需要配置一些额外的配置文件。这就导致我们使用外部配置文件时,相当于拥有了同一个类的两个单独的信息源,这经常导致代码的同步问题。同时,它也要求开发者必须同时指导如何编写Java程序,以及如何描述文件。

猜你喜欢

转载自blog.csdn.net/qq_33905217/article/details/109747037