Java Annotation学习笔记

简介: 作为一个早期短暂从事过C++开发工作的程序员,我个人认为Annotation可能是Java与C++语言较大的不同点之一,这也是一个前C++程序员由衷认为Java可能、或许、maybe要比C++更好用的原因之一。本文通过一个小例子展示了自定义注解的全过程。

作为一个早期短暂从事过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注解的元素类型

这里写图片描述

目录
相关文章
|
7月前
|
Java 编译器 开发工具
Java基础学习笔记——idea篇
JDK由JRE(包含JVM和核心类库)和开发工具箱(如javac编译器和java运行工具)组成。Java项目结构包括Project、Module、Package和Class。IDEA快捷键包括:生成main方法(main psvm)、复制代码(Ctrl+D)、删除代码(Ctrl+Y/X)、格式化代码(Ctrl+Alt+L)、重命名(Shift+F6)等。
59 0
|
4月前
|
存储 Java
Java学习笔记 List集合的定义、集合的遍历、迭代器的使用
Java学习笔记 List集合的定义、集合的遍历、迭代器的使用
|
1月前
|
Java 数据库连接 API
Spring 框架的介绍(Java EE 学习笔记02)
Spring是一个由Rod Johnson开发的轻量级Java SE/EE一站式开源框架,旨在解决Java EE应用中的多种问题。它采用非侵入式设计,通过IoC和AOP技术简化了Java应用的开发流程,降低了组件间的耦合度,支持事务管理和多种框架的无缝集成,极大提升了开发效率和代码质量。Spring 5引入了响应式编程等新特性,进一步增强了框架的功能性和灵活性。
49 0
|
3月前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(基础篇)
从Java环境的搭建到实际代码的编写,从基本用法的讲解到底层原理的剖析,深度解析Java基础知识。本文是《Java学习路线》专栏的起始文章,旨在提供一套完整的Java学习路线,覆盖Java基础知识、数据库、SSM/SpringBoot等框架、Redis/MQ等中间件、设计模式、架构设计、性能调优、源码解读、核心面试题等全面的知识点,并在未来不断更新和完善,帮助Java从业者在更短的时间内成长为高级开发。
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(基础篇)
|
3月前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(进阶篇)
本文是Java基础的进阶篇,对异常、集合、泛型、Java8新特性、I/O流等知识进行深入浅出的介绍,并附有对应的代码示例,重要的地方带有对性能、底层原理、源码的剖析。适合Java初学者。
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(进阶篇)
|
2月前
|
Java 数据安全/隐私保护
java学习笔记(基础习题)
java学习笔记(基础习题)
46 0
|
2月前
|
Java 程序员 开发工具
java学习笔记
java学习笔记
48 0
|
3月前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(高级篇)
本文是“Java学习路线”中Java基础知识的高级篇,主要对多线程和反射进行了深入浅出的介绍,在多线程部分,详细介绍了线程的概念、生命周期、多线程的线程安全、线程通信、线程同步,并对synchronized和Lock锁;反射部分对反射的特性、功能、优缺点、适用场景等进行了介绍。
|
4月前
|
SQL druid Java
Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)(下)
Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)
65 3
Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)(下)
|
4月前
|
SQL Java 关系型数据库
Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)(上)
Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)
187 3
Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)(上)