【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());
    }
}
目录
相关文章
|
19天前
|
XML Java 编译器
Java学习十六—掌握注解:让编程更简单
Java 注解(Annotation)是一种特殊的语法结构,可以在代码中嵌入元数据。它们不直接影响代码的运行,但可以通过工具和框架提供额外的信息,帮助在编译、部署或运行时进行处理。
85 43
Java学习十六—掌握注解:让编程更简单
|
14天前
|
Java 开发者 Spring
[Java]自定义注解
本文介绍了Java中的四个元注解(@Target、@Retention、@Documented、@Inherited)及其使用方法,并详细讲解了自定义注解的定义和使用细节。文章还提到了Spring框架中的@AliasFor注解,通过示例帮助读者更好地理解和应用这些注解。文中强调了注解的生命周期、继承性和文档化特性,适合初学者和进阶开发者参考。
38 14
|
14天前
|
前端开发 Java
[Java]讲解@CallerSensitive注解
本文介绍了 `@CallerSensitive` 注解及其作用,通过 `Reflection.getCallerClass()` 方法返回调用方的 Class 对象。文章还详细解释了如何通过配置 VM Options 使自定义类被启动类加载器加载,以识别该注解。涉及的 VM Options 包括 `-Xbootclasspath`、`-Xbootclasspath/a` 和 `-Xbootclasspath/p`。最后,推荐了几篇关于 ClassLoader 的详细文章,供读者进一步学习。
26 12
|
8天前
|
Java 编译器
Java进阶之标准注解
Java进阶之标准注解
20 0
|
1月前
|
JSON Java 数据库
java 常用注解大全、注解笔记
关于Java常用注解的大全和笔记,涵盖了实体类、JSON处理、HTTP请求映射等多个方面的注解使用。
34 0
java 常用注解大全、注解笔记
|
2月前
|
Arthas Java 测试技术
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
Java字节码文件、组成、详解、分析;常用工具,jclasslib插件、阿里arthas工具;如何定位线上问题;Java注解
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
|
26天前
|
IDE Java 编译器
java的反射与注解
java的反射与注解
16 0
|
2月前
|
Java 编译器 程序员
Java注解,元注解,自定义注解的使用
本文讲解了Java中注解的概念和作用,包括基本注解的用法(@Override, @Deprecated, @SuppressWarnings, @SafeVarargs, @FunctionalInterface),Java提供的元注解(@Retention, @Target, @Documented, @Inherited),以及如何自定义注解并通过反射获取注解信息。
Java注解,元注解,自定义注解的使用
|
1月前
|
XML Java 数据格式
Java-spring注解的作用
Java-spring注解的作用
20 0
|
2月前
|
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
下一篇
无影云桌面