JDK中的注解

本文详细介绍了Java注解的基础知识,包括标准注解如@Override、@Deprecated、@SuppressWarnings,元注解如@Target、@Retention、@Documented等。此外,文章还探讨了自定义注解的创建、注解处理器的使用,以及注解在运行时和编译期的作用,如Spring框架中的注解应用。通过示例展示了如何结合注解优化代码,如使用注解实现策略模式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、注解基础知识

自定义注解和反射的应用是框架的基础,在我们使用Spring时会发现,Spring为我们提供了大量的注解,这些注解简化了xml配置文件的使用,在我们平时开发中,也会自定义一些注解结合拦截器或者AOP的使用来优化我们的代码,因此,我们需要对Java注解有一个深入的认识。
注解(Annotation)也被称为元数据,是JavaSE5引入的重要特性之一

1 标准注解

Java在java.lang包中内置了三种标准注解

1.1 @Override

表示当前的方法定义将覆盖超类中的方法,即重写

1.2 @Deprecated

编译器会对注解有@Deprecated的元素发出警告信息,通常表示该元素有危险或者有更好的选择,不建议开发人员使用该元素,例如Thread类中的stop()

@Deprecated
public final void stop() {
}   

1.3 @SuppressWarnings

关闭不当的编译器警告信息
该注解使用必须添加参数,参数列表如下
例:

@SuppressWarnings(value = "all")
关键字用途
allto suppress all warnings (抑制所有警告)
boxingto suppress warnings relative to boxing/unboxing operations (抑制装箱、拆箱操作时候的警告)
castto suppress warnings relative to cast operations (抑制映射相关的警告)
dep-annto suppress warnings relative to deprecated annotation (抑制启用注释的警告)
deprecationto suppress warnings relative to deprecation (抑制过期方法警告)
fallthroughto suppress warnings relative to missing breaks in switch statements (抑制确在switch中缺失breaks的警告)
finallyto suppress warnings relative to finally block that don’t return (抑制finally模块没有返回的警告)
hidingto suppress warnings relative to locals that hide variable(抑制相对于隐藏变量的局部变量的警告)
incomplete-switchto suppress warnings relative to missing entries in a switch statement (enum case)(忽略没有完整的switch语句)
nlsto suppress warnings relative to non-nls string literals( 忽略非nls格式的字符)
nullto suppress warnings relative to null analysis( 忽略对null的操作)
rawtypesto suppress warnings relative to un-specific types when using generics on class params( 使用generics时忽略没有指定相应的类型)
restrictionto suppress warnings relative to usage of discouraged or forbidden references( 抑制禁止使用劝阻或禁止引用的警告)
serialto suppress warnings relative to missing serialVersionUID field for a serializable class( 忽略在serializable类中没有声明serialVersionUID变量)
static-accessto suppress warnings relative to incorrect static access( 抑制不正确的静态访问方式警告)
synthetic-accessto suppress warnings relative to unoptimized access from inner classes( 抑制子类没有按最优方法访问内部类的警告)
uncheckedto suppress warnings relative to unchecked operations( 抑制没有进行类型检查操作的警告)
unqualified-field-accessto suppress warnings relative to field access unqualified( 抑制没有权限访问的域的警告)
unusedto suppress warnings relative to unused code( 抑制没被使用过的代码的警告)

2 元注解

元注解主要是负责注解其他注解
JavaSE5定义了四种元注解,从Java7开始额外添加了三种

2.1 @Target

表示该注解可以用在什么地方
ElementType取值如下

ElementType取值范围
ElementType.ANNOTATION_TYPE可以给一个注解进行注解
ElementType.CONSTRUCTOR可以给构造方法进行注解
ElementType.FIELD可以给属性进行注解
ElementType.LOCAL_VARIABLE可以给局部变量进行注解
ElementType.METHOD可以给方法进行注解
ElementType.PACKAGE可以给一个包进行注解
ElementType.PARAMETER可以给一个方法内的参数进行注解
ElementType.TYPE可以给一个类型进行注解,比如类、接口、枚举

2.2 @Retention

表示需要在什么级别保存该注解信息,RetentionPolicy取值如下

RetentionPolicy取值生命周期
RetentionPolicy.SOURCE注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
RetentionPolicy.CLASS注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
RetentionPolicy.RUNTIME注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。

基本上在我们的开发中都是使用RUNTIME

2.3 @Documented

将此注解包含在Javadoc中

2.4 @Inherited

允许子类继承父类中的注解
例如:

@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface TestInherited(){
}

@TestInherited
public class A{
}

public class B extend A{
}

上面的例子中B也被@TestInherited注解

2.5 @SafeVarargs

Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告

2.6 @FunctionalInterface

Java 8 开始支持,标识一个匿名函数或函数式接口

2.7 @Repeatable

Java 8 开始支持,标识某注解可以在同一个声明上使用多次
例如:一个人可能同时具有多个职业

@Repeatable(Persons.class)
@interface Person{
   String value() default "";
}

@interface  Persons{
   Person[] value();
}

@Person("gamer)
@Person("programmer")
class MaoJian{
}

@Repeatable后面括号的类是一个容器注解,容器注解就是存放其他注解的地方,它本身也是一个注解
按照规定,它里面必须有个value属性,属性类型是一个被@Repeatable注解过的注解数组

3 自定义注解

使用@interface自定义注解时,会自动继承java.lang.annotation.Annotation接口

3.1 注解的格式

public @Interface 注解名

3.2 注解的属性

注解的属性也叫做成员变量,注解只有成员变量,没有方法,注解的成员变量在注解的定义中,以无形参的方法的形式来声明,其方法名定义了成员变量的名称,返回值定义了成员变量的类型,默认值用default来指定,注解的属性值类型必须是八种基本数据类型及类,接口,注解以及他们的数组

3.3 示例

现在给出一种自定义注解的例子,使用自定义注解结合策略模式去掉if-else
如果我们要对一个数组进行排序,那么排序算法就有多种选择

3.3.1 自定义策略注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface SortHandler {
    String value();
}
3.3.2 注解的参数取值

也就是if else的类型,示例列举计数排序,插入排序,希尔排序三种

public interface SortType {
    String counter = "1";
    String insertion = "2";
    String shell = "3";
}
3.3.3 定义所有的类型策略
public interface Sort {
    void sort();
}
@SortHandler(SortType.counter)
@Component
public class CounterSort implements Sort {
    @Override
    public void sort() {
        System.out.println("计数排序");
    }
}
@SortHandler(SortType.insertion)
@Component
public class InsertSort implements Sort {
    @Override
    public void sort() {
        System.out.println("插入排序");
    }
}
@Component
@SortHandler(SortType.shell)
public class ShellSort implements Sort {
    @Override
    public void sort() {
        System.out.println("希尔排序");
    }
}
3.3.4 将策略实现类添加到spring容器中
@Component
public class SortHandlerContext {

    @Resource
    private ApplicationContext applicationContext;

    public static Map<String,Class<Sort>> sortHandlerMap = new HashMap<>();

    public Sort getSort(String type){
        Class<Sort> sortClass = sortHandlerMap.get(type);
        if (sortClass == null){
            throw new IllegalArgumentException("没有与之对应的排序算法");
        }
        return applicationContext.getBean(sortClass);
    }
}
@Component
public class SortHandlerProcessor implements ApplicationContextAware {
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String,Object> sortMap = applicationContext.getBeansWithAnnotation(SortHandler.class);
        sortMap.forEach((k,v)->{
            Class<Sort> sort = (Class<Sort>) v.getClass();
            String type = sort.getAnnotation(SortHandler.class).value();
            SortHandlerContext.sortHandlerMap.put(type,sort);
        });
    }
}
3.3.5 Service业务逻辑
public interface SortService {
    void sort(String type);
}
@Service
public class SortServiceImpl implements SortService {

    @Autowired
    private SortHandlerContext sortHandlerContext;

    @Override
    public void sort(String type) {
        sortHandlerContext.getSort(type).sort();
    }
}
3.3.6 测试
public class AnnoTest {
    @Test
    public void test(){
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext("io.github.maojian.demo");
        applicationContext.getBean(SortServiceImpl.class).sort("1");
    }
}

运行结果如下图所示
在这里插入图片描述

二、注解处理器

如果没有用来读取注解的工具,那么注解也不会比注释更有用
注解分为两种:一种是运行时注解,一种是编译期注解

1 运行时注解

运行时注解处理相对比较简单,利用JavaSE5提供的反射机制API在运行时获取注解信息就可以了
在Spring中常用的注解有一个@Value,它的作用是读取配置文件对应的属性并且赋值给属性,我们实现一个赋值功能,省略读取配置文件属性的功能
定义注解

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyValue {
    String value();
}

使用注解

public class UseMyValue {
    @MyValue("素还真")
    private String userName;

    @Override
    public String toString() {
        return "UseMyValue{" +
                "userName='" + userName + '\'' +
                '}';
    }
}

利用反射赋值及测试

@Test
    public void testRef() throws NoSuchFieldException, IllegalAccessException {
        Field field = UseMyValue.class.getDeclaredField("userName");
        UseMyValue useMyValue = new UseMyValue();
        field.setAccessible(true);
        if (field.isAnnotationPresent(MyValue.class)){
            MyValue myValue = field.getAnnotation(MyValue.class);
            String userName = myValue.value();
            field.set(useMyValue,userName);
        }

        System.out.println(useMyValue);
    }

运行结果
在这里插入图片描述

2 编译期注解

编译时解析有两种机制,分别简单描述下:

2.1 Annotation Processing Tool

apt自JDK5产生,JDK7已标记为过期,不推荐使用,JDK8中已彻底删除,自JDK6开始,可以使用Pluggable Annotation Processing API来替换它,apt被替换主要有2点原因:
1.api都在com.sun.mirror非标准包下
2.没有集成到javac中,需要额外运行

2.2 Pluggable Annotation Processing API

JSR 269.自JDK6加入,作为apt的替代方案,它解决了apt的两个问题,javac在执行的时候会调用实现了该API的程序,这样我们就可以对编译器做一些增强,这时javac执行的过程如下:
在这里插入图片描述
在我们的开发中使用的插件lombok就是基于JSR 269开发的,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值