Java Annotation学习笔记

作为一个早期短暂从事过C++开发工作的程序员,我个人认为Annotation可能是Java与C++语言较大的不同点之一,这也是一个前C++程序员由衷认为Java可能、或许、maybe要比C++更好用的原因之一。二十多年来,Java一直保持着更新,不断完善并与时俱进,这可能是其多年来独领编程语言之风骚的重要原因。不多扯,入正题。(编程知识的学习,我一般会遵循这样的一个过程:先熟悉基本概念,再来个小程序跑起来看看,最后理论与程序相结合,加深认识并总结记录。)

1、什么是Annotation



Annotation被译作“注解”,标准的英译汉,但是这个译词并没有很好的反映Annotation在java中的意义,或许“标记”更好一些,也或许“标签”。

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

注解是那些插入到源代码中使用其他工具可以对其进行处理的标签。——《Java核心技术卷2》

从《Java核心技术卷2》的定义来看,注解包含两点内容:其一,它是标签、标记;此外,这个标签(标记)可以使用其他工具进行处理。
那么,其他工具在哪里可以处理这些标签呢?一个是源码层,另一个是类文件。

注解不会改变程序的编译方式,对于包含注解和不包含注解的代码,Java编译器会生成相同的虚拟机指令。——《Java核心技术卷2》

综上,大致可以给出Java注解一个简单通俗的描述使用过程:定义标签,然后提供标签处理工具,最后是应用标签到其它的代码中。当然,Java自带的注解,我们就只需要直接使用就可以了,因为定义和处理工具Java都帮我们做好了。

2、来个sample


2.1定义标签

import java.lang.annotation.*;

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

    public int id();

    public String description() default "No description";
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
Target和Retention是java语言自带的两个元注解,Target表示用户自定义的注解(UseCase)能应用在哪里(注解类型声明,包,类或接口、方法等);
Retention用来指明自定义注解应该保留多长的时间(取值为SOURCE/CLASS/RUNTIME)
public @interface UseCase
这是定义注解的语法形式,乍一看类似接口的定义,区别在于interface前加了一个@符号,后续使用该用户自定义注解时语法就是@UseCase
public int id();
public String description() default "No description";
这是注解中定义字段的语法,()并不表示这是一个方法,再者,注解中不支持定义方法。
同时,可以设置字段的默认值,通过default关键字。没有默认值的字段必须在使用时显示指明。

2.2 标签处理工具

import java.lang.reflect.*;

public class UseCaseTracker {

    public static void trackUseCase(Class<?> cl) {
        for (Method m : cl.getMethods()) {
            UseCase useCase = m.getAnnotation(UseCase.class);
            if (useCase != null) {
                System.out.println("Found Use Case : " + useCase.id() + " " + useCase.description());
            }
        }
    }

}
@Retention(RetentionPolicy.RUNTIME)中的RUNTIME使得注解可以保留到类文件中,并由虚拟机载入,这样我们就可以通过反射API来获得它们。接下来我就是通过反射API获得这些注解。
public static void trackUseCase(Class<?> cl) {
    for (Method m : cl.getMethods()) {
        UseCase useCase = m.getAnnotation(UseCase.class);
        if (useCase != null) {
            System.out.println("Found Use Case : " + useCase.id() + " " + useCase.description());
        }
    }
}
trackUseCase方法通过传入应用了UseCase注解的类,再通过反射API获得这些注解并打印注解中的信息。

2.3 应用标签

import java.util.List;

public class PasswordUtils {

    @UseCase(id=47, description="Passwords must contain at least one numeric")
    public boolean validatePassword(String password) {
        return (password.matches("\\w\\d\\w"));
    }

    @UseCase(id=48)
    public String encryptPassword(String password) {
        return new StringBuilder(password).reverse().toString();
    }

    @UseCase(id=49, description="New passwords can't equal previously used ones")
    public boolean checkForNewPassword(List<String> prePasswords, String password) {
        return !prePasswords.contains(password);
    }

}
@Target(ElementType.METHOD)规定了注解只能是应用在方法上,因此,上述例子中便是如此。
@UseCase(id=47, description="Passwords must contain at least one numeric")
@UseCase(id=48)
@UseCase(id=49, description="New passwords can't equal previously used ones")
以上是三种使用应用自定义注解的例子。

2.4 测试

import java.lang.reflect.*;

public class UseCaseTracker {

    public static void trackUseCase(Class<?> cl) {
        for (Method m : cl.getMethods()) {
            UseCase useCase = m.getAnnotation(UseCase.class);
            if (useCase != null) {
                System.out.println("Found Use Case : " + useCase.id() + " " + useCase.description());
            }
        }
    }

    public static void main(String[] args) {
        trackUseCase(PasswordUtils.class);
    }

}

直接在注解处理工具中加了个main方法来测试注解处理工具。输出结果如下:

Found Use Case : 47 Passwords must contain at least one numeric
Found Use Case : 49 New passwords can't equal previously used ones
Found Use Case : 48 No description

注:以上代码来自《Java编程思想》,部分有改动。

3、归纳整理


3.1 注解元素(属性)的类型

基本类型(int/short/long/byte/char/double/float/boolean)
String
Class
enum
注解类型
以上五种类型组成的数组

注:注解元素值不能为null,默认值也不能为null。

3.2 标准注解

3.3 Target注解的元素类型

元素类型定义在枚举类星Element中。

3.4 Retention注解的元素类型

时间: 2024-11-08 21:15:06

Java Annotation学习笔记的相关文章

java基础学习笔记之反射_java

反射 反射:将类的属性和方法映射成相应的类. 反射基本使用 获取Class类的三种方法: 类名.class 对象名.getClass() Class.forName("要加载的类名") 根据API写就行了,大致流程就是: 用上述三种方式之一获取特定类的Class类,即该类对应的字节码 调用Class对象的getConstructor(Class<?>... parameterTypes)获取构造方法对象 调用是构造方法类Constructor的newInstance(Obj

java基础学习笔记之泛型_java

泛型 将集合中的元素限定为一个特定的类型. 术语 ArrayList<E> -- 泛型类型 ArrayList -- 原始类型 E -- 类型参数 <> -- 读作"typeof" ArrayList<Integer> -- 参数化的类型 Integer -- 实际类型参数 几点注意: 参数化类型和原始类型相互兼容 ArrayList collection1 = new ArrayList<Integer>();//通过,无warning

Java --Annotation学习心得体会及笔记

相对于注释这种给程序员看的信息: 注解,就是给程序看的解释性的语言,其作用就相当于配置文件的存在.其存在的意义在于以下几点: 优点: 方便的使程序员看到相关项的关联位置及关联方式等信息. 缺点: 由于注解是存在于程序之上的,所以每次对注解进行修改后就必须要对源代码进行重新编译才会生效. 注解都长什么样呢? 在使用Eclipse编程的时候我们会经常看到下面几种: @Override--覆盖父类的方法 @SupportWarning--抑制警告 @Deprecated--过时的 大致的使用方法,我从

《Effective Java》学习笔记(4)

笔记 五.C语言结构的替代 如何用java模拟c语言中的一些结构 第19条:用类代替结构     对于C语言中的结构,java可以用类来代替,不过你不应让类的成员变量可以被公开访问,成员变量应该是private,然后提供一些读写操作来操纵这些变量,想比于C或者C++里的结构类型,这样的类更为安全,体现了OOP的封装性.示例代码 public class Point{     private float x;    private float y;     public float getX(){

java虚拟机学习笔记

笔记 1.编译顺序:                 编译器                     虚拟机      虚拟机          java源文件*.java------->字节码*.class------>类装载器--->执行引擎 一个.class文件只能包含一个类或接口.因此.java文件中定义了多少类,编译时就会生成多少.class文件(内部类不算). 2.java程序可以选择两种方式访问底层系统,由程序员选择:(1).通过java程序调用javaapi调用本地方法,

java虚拟机学习笔记1

笔记 1.编译顺序:                 编译器                     虚拟机      虚拟机          java源文件*.java------->字节码*.class------>类装载器--->执行引擎 一个.class文件只能包含一个类或接口.因此.java文件中定义了多少类,编译时就会生成多少.class文件(内部类不算). 2.java程序可以选择两种方式访问底层系统,由程序员选择:(1).通过java程序调用javaapi调用本地方法,

java虚拟机学习笔记2

笔记 11.数组数组也是类的对象.具有相同类型和维数的数组属于同一个类(不管长度只看维数).数组的长度属于对象实例.多维数组也是一维数组.如二 维数组,即为一个一维数组,该一维数组的每个元素是一个数组的引用.数组和普通对象一样也存储在堆中.数组名为数组的引用,通过索引即数组标号来访问数组内容. 12.异常在java栈帧的帧数据区内保存有针对该方法的异常表的引用.异常表记载了该方法的字节码(*.class)受catch子句保护的范围(即try子句里的 字节码).当某个方法抛出异常时,虚拟机在对应的

Java核心技术学习笔记

掌握Java核心技术是学习和掌握好Java技术的关键,下边分17个点对这些Java核心技术进行讲解. 1.Java中没有多继承,而是用接口来代替多继承 2.运行一个已经编译的程序时,Java解释器总是从指定类的main方法中的代码开始执行,因此,执行代码中必须有一个main函数. 3.Java是典型的强类型语言,即必须声明变量的类型,Java中有8种类型,6种数值类型(4个整数型和2个浮点型).一个字符类型和一个boolean类型. 4.强制类型转换: int nx = (int) x; //

JAVA NIO学习笔记1 - 架构简介

最近项目中遇到不少NIO相关知识,之前对这块接触得较少,算是我的一个盲区,打算花点时间学习,简单做一点个人学习总结. 简介 NIO(New IO)是JDK1.4以后推出的全新IO API,相比传统IO方式NIO采用了全新的底层I/O模型.传统IO的设计概念是面向流,而NIO则是面向块.简单点说,传统I/O是基于字节的,所有I/O都被视为单个字节的移动,使用时需先把对象转换为字节码:而NIO是面向块的,以块为单位处理数据,每个操作会生成或消费一个块的数据.从设计理念来看,NIO的操作粒度要比传统I