【Java 第八篇章】注解

简介: 从JDK5起,Java引入注解作为元数据支持,区别于注释,注解可在编译、类加载和运行时被读取处理。注解允许开发者在不影响代码逻辑的前提下嵌入补充信息。核心概念包括`Annotation`接口、`@Target`定义适用范围如方法、字段等,`@Retention`设定生命周期,如仅存在于源码或运行时可用。Java提供了内置注解如`@Override`用于检查方法重写、`@Deprecated`标记废弃元素、`@SuppressWarnings`抑制警告。自定义注解可用于复杂场景,例如通过反射实现字段验证。

一、简介

1、从JDK5开始,Java增加对元数据的支持,也就是注解,注解与注释是有一定区别的,可以把注解理解为代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过注解开发人员可以在不改变原有代码和逻辑的情况下在源代码中嵌入补充信息。

2、提供一种为程序元素设置元数据的方法。

3、注解不能直接干扰程序代码运行,无论增加或者删除注解,代码都能正常运行。

二、注解的核心

1、Annotation

所有 annotation 类型都要扩展的公共接口。注意,手动扩展该公共接口的接口不定义 annotation 类型。还要注意此接口本身不定义 annotation 类型。

package java.lang.annotation;

public interface Annotation {
   

    boolean equals(Object obj);

    int hashCode();

    String toString();

    Class<? extends Annotation> annotationType();

}

2、@Target

设置注解的使用范围。

package java.lang.annotation;

public enum ElementType {
   
    //使用范围的可选值
    TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR,
    LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE,
    TYPE_PARAMETER, TYPE_USE
}
名称 作用
TYPE 接口、类、枚举、注解
FIELD 字段、枚举的常量
METHOD 方法
PARAMETER 方法参数
CONSTRUCTOR 构造函数
LOCAL_VARIABLE 局部变量
ANNOTATION_TYPE 注解
PACKAGE 所属包
TYPE_PARAMETER
TYPE_USE

3、@Retention

设置生命周期。

package java.lang.annotation;

public enum RetentionPolicy {
   
    //设置声明周期的可选值
    SOURCE, CLASS, RUNTIME
}
名称 作用
SOURCE 注解仅存在于源码中,在class字节码文件中不包含。
CLASS 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得。
RUNTIME 注解会在class字节码文件中存在,在运行时可以通过反射获取到。

三、内置注解

1、@Override之方法重写

检测方法是否是重写方法。如果父类中或者引用的接口中没有该方法,在编译时期就会报错。

package java.lang;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
   
}

案例

// WoNiu 类
package com.tsing.annotation;
public class WoNiu {
   
    private String name = "蜗牛";
    public String sayHello() {
   
        return "我的名字是" + this.name;
    }
}

// SuperWoNiu 类
package com.tsing.annotation;
public class SuperWoNiu extends WoNiu{
   
    private String name = "极速蜗牛";
    @Override
    public String sayHello() {
   
        System.out.println(super.sayHello());
        return "我是" +  this.name;
    }
}

// 测试类
package com.tsing.annotation;
public class TestOverride {
   
    public static void main(String[] args) {
   
        SuperWoNiu sw = new SuperWoNiu();
        System.out.println(sw.sayHello());
    }
}

//输出结果
我的名字是蜗牛
我是极速蜗牛

2、@Deprecated之是否过时

表示该类或者该方法已经不建议使用,可以选择其他更好的方法使用。


package java.lang;

import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={
   CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
   
}

案例

// WoNiu 类
package com.tsing.annotation.deprecated;

public class WoNiu {
   
    private String name = "蜗牛";
    @Deprecated
    public void sayHello() {
   
        System.out.println(this.name + "已经过时了!!!!");
    }
}

// 测试类
package com.tsing.annotation.deprecated;
public class DemoDeprecated {
   
    public static void main(String[] args) {
   
        WoNiu woNiu = new WoNiu();
        woNiu.sayHello();
    }
}

//运行结果
蜗牛已经过时了!!!!

3、@SuppressWarnings之异常压制

取消显示指定的编译器警告。

package java.lang;

import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;

@Target({
   TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
   
    String[] value();
}

案例

package com.tsing.annotation.suppressWarnings;

import java.util.ArrayList;
import java.util.List;

public class WoNiu {
   

    private String name = "蜗牛";

    //这种方式声明集合,add方法不会出现异常警告。
    public static List<String> list1 = new ArrayList<String>();

    //这种方式声明集合,add方法会出现异常警告。
    public static List cache = new ArrayList();

    // 添加上这边注解,就可以将异常警告信息压制。
    @SuppressWarnings(value = "unchecked")
    public void add(String data) {
   
        boolean add = cache.add(data);
    }

}

四、自定义注解

定义注解类

package com.tsing.annotation.cases;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WoNiu {
   
    String desc();
    int length();
}

通过反射获取设置的值

package com.tsing.annotation.cases;

import org.junit.Test;

import java.lang.reflect.Field;

public class DemoAnnotation {
   
    //使用自定义注解
    @WoNiu(desc = "用户名", length = 12)
    private String username;

    @Test
    public void testWoNiu() {
   

        Class demoAnnotationClass = DemoAnnotation.class;

        //获取所有字段
        for (Field f : demoAnnotationClass.getDeclaredFields()) {
   
            //判断这个字段是否有注解
            if(f.isAnnotationPresent(WoNiu.class)) {
   
                WoNiu woNiu = f.getAnnotation(WoNiu.class);
                System.out.println("字段:[" + f.getName() + "], 描述:[" + woNiu.desc() + "], 长度:[" + woNiu.length() +"]");
            }
        }
    }
}

五、注解的使用场景

解决对象中校验问题,将复杂的校验判断存到对象中

package com.tsing.annotation.cases.controlflow.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Length {
   
    int min();
    int max();
    String msg();
}

package com.tsing.annotation.cases.controlflow.annotation;

import java.lang.reflect.Field;

public class Bean {
   

    public static String validate(Object obj) throws IllegalAccessException {
   
        /**
         * 通过反射获取object中有哪些字段
         * 本文获取的 id、name、sex、bool几个字段
         */

        Field[] fields = obj.getClass().getDeclaredFields();

        // for 循环校验每个字段上那个字段有注解
        for (Field field : fields) {
   
            // 判断那个字段上有@Length注解
            if(field.isAnnotationPresent(Length.class)) {
   
                // 通过反射获取该字段上length注解的详细信息
                Length length = field.getAnnotation(Length.class);
                // 设置可以访问私有变量
                field.setAccessible(true);

                // 获取实际的值
                int value = (int) field.get(obj);
                System.out.println(value);
                // 将实际的值和注解上标识的值比较
                    // 10  < 11  && 4000 > 11
                if(!(value > length.min() && value < length.max())){
   
                    return length.msg();
                }
            }
        }

        return null;
    }
}

package com.tsing.annotation.cases.controlflow;

import com.tsing.annotation.cases.controlflow.annotation.Length;

public class WoNiu {
   

    @Length(min = 10, max = 4000, msg = "id 是10 -- 4000")
    private  Integer id;

    private  String name;

    private  String sex;

    private  Boolean bool;

    public WoNiu() {
   
    }

    public WoNiu(Integer id, String name, String sex, Boolean bool) {
   
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.bool = bool;
    }

    public Integer getId() {
   
        return id;
    }

    public void setId(Integer id) {
   
        this.id = id;
    }

    public String getName() {
   
        return name;
    }

    public void setName(String name) {
   
        this.name = name;
    }

    public String getSex() {
   
        return sex;
    }

    public void setSex(String sex) {
   
        this.sex = sex;
    }

    public Boolean getBool() {
   
        return bool;
    }

    public void setBool(Boolean bool) {
   
        this.bool = bool;
    }

    @Override
    public String toString() {
   
        return "WoNiu{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", bool=" + bool +
                '}';
    }
}

package com.tsing.annotation.cases.controlflow;
import com.tsing.annotation.cases.controlflow.annotation.Bean;
public class Demo {
   
    public static void main(String[] args) throws IllegalAccessException {
   
        WoNiu woNiu = new WoNiu();
        woNiu.setId(9);
        woNiu.setBool(false);
        woNiu.setSex("男");
        String validate = Bean.validate(woNiu);
        System.out.println(validate);
        System.out.println(woNiu.toString());
    }
}
目录
相关文章
|
2月前
|
Java Maven 编译器
Java编译器注解运行和自动生成代码问题之@AutoService工作问题如何解决
Java编译器注解运行和自动生成代码问题之@AutoService工作问题如何解决
113 1
|
2月前
|
Java API 编译器
Java编译器注解运行和自动生成代码问题之编译时通过参数设置选项值问题如何解决
Java编译器注解运行和自动生成代码问题之编译时通过参数设置选项值问题如何解决
|
10天前
|
Arthas Java 测试技术
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
Java字节码文件、组成、详解、分析;常用工具,jclasslib插件、阿里arthas工具;如何定位线上问题;Java注解
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
|
1天前
|
Java 数据库连接 数据格式
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
IOC/DI配置管理DruidDataSource和properties、核心容器的创建、获取bean的方式、spring注解开发、注解开发管理第三方bean、Spring整合Mybatis和Junit
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
|
4天前
|
Java 编译器 测试技术
|
15天前
|
存储 JSON 前端开发
【Java】用@JsonFormat(pattern = “yyyy-MM-dd“)注解,出生日期竟然年轻了一天
在实际项目中,使用 `@JsonFormat(pattern = &quot;yyyy-MM-dd&quot;)` 注解导致出生日期少了一天的问题,根源在于夏令时的影响。本文详细解析了夏令时的概念、`@JsonFormat` 注解的使用方法,并提供了三种解决方案:在注解中添加 `timezone = GMT+8`、修改 JVM 参数 `-Duser.timezone=GMT+08`,以及使用 `timezone = Asia/Shanghai
11 0
【Java】用@JsonFormat(pattern = “yyyy-MM-dd“)注解,出生日期竟然年轻了一天
|
24天前
|
Java
Java系列之 IDEA 为类 和 方法设置注解模板
这篇文章介绍了如何在IntelliJ IDEA中为类和方法设置注解模板,包括类模板的创建和应用,以及两种不同的方法注解模板的创建过程和实际效果展示,旨在提高代码的可读性和维护性。
|
24天前
|
存储 缓存 Java
Java本地高性能缓存实践问题之使用@CachePut注解来更新缓存中数据的问题如何解决
Java本地高性能缓存实践问题之使用@CachePut注解来更新缓存中数据的问题如何解决
|
2月前
|
编译器 Java
Java编译器注解运行和自动生成代码问题之重写init方法的问题如何解决
Java编译器注解运行和自动生成代码问题之重写init方法的问题如何解决
|
2月前
|
Java 测试技术 Maven
Java编译器注解运行和自动生成代码问题之在编译时需要设置-proc:none参数问题如何解决
Java编译器注解运行和自动生成代码问题之在编译时需要设置-proc:none参数问题如何解决