Java基础——十、注解

17 篇文章 0 订阅
10 篇文章 0 订阅

十、注解

一、简介

**Java 注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。**注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用

注解(Annotations)是Java中的一种元数据提供机制,它可以在代码中标记类、方法、变量、参数等,从而**在编译时或运行时为代码提供额外的信息。**注解不会直接影响代码的执行逻辑,但可以通过编译器或框架工具进行解析和处理,从而实现自动配置、代码生成、验证等功能。

注解在Java 5中引入,随着Java的发展,注解的使用变得越来越广泛,尤其是在框架如Spring等中,注解大大简化了开发工作。

二、自定义注解

Java允许开发者创建自己的注解,通常用于标记特定的行为或属性。自定义注解可以通过元注解(meta-annotations)来定义其行为。常用的元注解包括:

  • @Target:指定注解可以应用的程序元素,如类、方法、字段等。
  • @Retention:指定注解的生命周期,是只在源码中保留,还是编译后存在,或者在运行时依然有效。
  • @Inherited:指定注解是否可以被子类继承。
  • @Documented:指示注解是否应当包含在JavaDoc中。

例如,创建一个自定义注解@MyAnnotation

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

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "default value";
}

三、工作中的应用

1.注解简介

注解(Annotations)是Java中的一种元数据提供机制,它可以在代码中标记类、方法、变量、参数等,从而在编译时或运行时为代码提供额外的信息。注解不会直接影响代码的执行逻辑,但可以通过编译器或框架工具进行解释和处理,从而实现自动化配置、代码生成、验证等功能

注解在Java 5中引入,随着Java的发展,注解的使用变得越来越广泛,尤其是在框架如Spring、Hibernate等中,注解大大简化了开发工作。

注解的本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。而我们通过反射获取注解时,返回的是Java运行时生成的动态代码对象$Proxy1。通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandlerinvoke方法。该方法会从memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池。

2.自定义注解

Java允许开发者创建自己的注解,通常用于标记特定的行为或属性。自定义注解可以通过元注解(meta-annotations)来定义其行为。常用的元注解包括:

  • @Target:指定注解可以应用的程序元素,如类、方法、字段等。
  • @Retention:指定注解的生命周期,是只在源码中保留,还是编译后存在,或者在运行时依然有效。
  • @Inherited:指示注解是否可以被子类继承。
  • @Documented:指示注解是否应当包含在JavaDoc中。

例如,创建一个自定义注解@MyAnnotation

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

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "default value";
}
3.实际工作中常见的注解

在实际工作中,尤其是使用Spring等框架时,以下注解非常常见:

  1. @Override
    标记方法是重写父类或接口的方法,编译器会对此进行检查,确保该方法确实覆盖了父类的方法。
  2. @Deprecated
    标记某个类、方法或字段已经不再推荐使用,通常会提供替代方案。
  3. @SuppressWarnings
    抑制编译器的警告信息。常用于抑制类型安全相关的警告,如未检查的类型转换。
  4. @Entity (JPA)
    用于标记一个类是JPA实体类,与数据库中的表对应。
  5. @Autowired (Spring)
    用于自动注入Spring容器中的Bean。Spring会根据类型自动寻找匹配的Bean,并将其注入到需要的地方。
  6. @RequestMapping (Spring MVC)
    用于映射HTTP请求到特定的控制器方法。可以指定请求路径、方法类型(GET、POST等)等信息。
  7. @Transactional (Spring)
    声明事务管理,Spring会根据配置自动管理数据库事务的提交和回滚。
  8. @Configuration (Spring)
    用于标记配置类,Spring会将其视为XML配置文件的替代,允许在类中定义Bean。
  9. @RestController (Spring)
    @Controller@ResponseBody的组合注解,用于处理RESTful Web服务,直接返回JSON或XML等数据格式。
4.举例

注解 Annotation 实现原理与自定义注解例子

自定义注解类编写的一些规则

  1. Annotation型定义为@interface,所有的Annotation自动继承java.lang.Annotation这一接口,并且不能再继承别的类或是接口
  2. 参数成员**只能用public或默认(default)**这两个访问权修饰。
  3. 参数成员只能用基本类型byteshortcharintlongfloatdoubleboolean八种基本类型和StringEnumClassannotations等数据类型,以及这一些类型的数组。
  4. 要获取类方法和字段的注解信息,必须通过Java的反射技术来获取Annotation对象,因为你除此之外没有别的获取注解对象的方法。
  5. 注解也可以没有定义成员,不过这样的注解就没啥用了。

注:自定义注解需要使用到元注解。

自定义注解实例

FruitName.java
/**
 * FruitName是一个用于标识水果名称的注解接口
 * 该注解用于标记一个字段,表示该字段存储了水果的名称
 * 通过此注解,可以在运行时获取到被注解的字段上的水果名称信息
 *
 * @author LiHao
 * @see ElementType#FIELD 注解的目标是字段
 * @see RetentionPolicy#RUNTIME 注解的保留策略为运行时,可以在运行时被读取
 * @see Documented 将该注解包含进API文档中
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
    /**
     * 定义注解的属性value,用于指定默认的水果名称
     * 如果未指定具体的水果名称,则默认值为空字符串
     *
     * @return 默认的水果名称
     */
    String value() default "";
}

FruitColor.java
import java.lang.annotation.*;

/**
 * @author: LiHao
 * @description: 水果颜色注解
 * thinking:这是一个自定义注解FruitColor,用于表示水果的颜色。它具有以下特点:
 * 1、使用@Target(ElementType.FIELD)指定该注解只能用于字段。
 * 2、使用@Retention(RetentionPolicy.RUNTIME)指定该注解在运行时可见。
 * 3、使用@Documented指定该注解应被生成到API文档中。
 * 4、该注解包含一个名为fruitColor的方法,用于指定水果的颜色,默认值为GREEN。
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
    /**
     * 颜色枚举
     */
    public enum Color {
        BLUE, RED, GREEN
    }

    /**
     * 颜色属性
     *
     * @return 默认颜色为GREEN
     */
    Color fruitColor() default Color.GREEN;
}
FruitProvider.java
import java.lang.annotation.*;

/**
 * @author: LiHao
 * @description: 水果供应者注解
 * thinking:此Java元注解定义了一个名为FruitProvider的注解,可用于字段上,并可在运行时反射获取。它包含:
 * 1、默认编号-1的id;
 * 2、默认为空字符串的name;
 * 3、默认为空字符串的address。 这些信息可通过Javadoc访问。
 */
@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface FruitProvider {
    /**
     * 获取供应商编号
     *
     * @return 供应商编号,默认为-1
     */
    public int id() default -1;

    /**
     * 获取供应商名称
     *
     * @return 供应商名称,默认为空字符串
     */
    public String name() default "";

    /**
     * 获取供应商地址
     *
     * @return 供应商地址,默认为空字符串
     */
    public String address() default "";
}
FruitInfoUtil.java
import java.lang.reflect.Field;

/**
 * @author: LiHao
 * @description: 注解处理器
 * thinking:该方法getFruitInfo通过反射获取传入类的所有字段,并检查字段上是否存在特定注解:
 * 1、若存在@FruitName注解,则输出水果名称。
 * 2、若存在@FruitColor注解,则输出水果颜色。
 * 3、若存在@FruitProvider注解,则输出供应商信息,包括编号、名称和地址。
 */

/**
 * FruitInfoUtil类用于提取水果相关信息
 * 通过使用反射机制,本类的方法能够从动态传入的类中提取关于水果名称、颜色和供应商的信息
 *
 */
public class FruitInfoUtil {
    /**
     * 从给定类中提取水果相关信息
     * 本方法通过检查类的字段上是否标注有特定的注解(FruitName, FruitColor, FruitProvider),
     * 来决定需要提取哪些信息,并将这些信息打印出来
     *
     * @param clazz 一个Class对象,表示从哪个类中提取水果信息
     */
    public static void getFruitInfo(Class<?> clazz) {
        // 初始化用于存储水果名称、颜色和供应商信息的字符串
        String strFruitName = " 水果名称:";
        String strFruitColor = " 水果颜色:";
        String strFruitProvider = "供应商信息:";

        // 获取传入类的所有字段
        Field[] fields = clazz.getDeclaredFields();

        // 遍历所有字段,检查并提取标注了特定注解的字段信息
        for (Field field : fields) {
            // 检查字段是否标注了FruitName注解
            if (field.isAnnotationPresent(FruitName.class)) {
                // 如果有,提取并打印水果名称
                FruitName fruitName = field.getAnnotation(FruitName.class);
                strFruitName += fruitName.value();
                System.out.println(strFruitName);
            } else if (field.isAnnotationPresent(FruitColor.class)) {
                // 检查字段是否标注了FruitColor注解
                // 如果有,提取并打印水果颜色
                FruitColor fruitColor = field.getAnnotation(FruitColor.class);
                strFruitColor = strFruitColor + fruitColor.fruitColor();
                System.out.println(strFruitColor);
            } else if (field.isAnnotationPresent(FruitProvider.class)) {
                // 检查字段是否标注了FruitProvider注解
                // 如果有,提取并打印供应商信息
                FruitProvider fruitProvider = field.getAnnotation(FruitProvider.class);
                strFruitProvider = strFruitProvider + " 供应商编号:" + fruitProvider.id() + " 供应商名称:" + fruitProvider.name() + " 供应商地址:" + fruitProvider.address();
                System.out.println(strFruitProvider);
            }
        }
    }
}
Apple.java
import lombok.Data;

/**
 * @author: LiHao
 * @description: 注解使用
 * thinking:这段 Java 代码定义了一个名为 Apple 的类,并使用了 Lombok 库的 @Data 注解来自动生成 getter、setter、equals、hashCode 和 toString 方法。
 * 1、@FruitName("Apple") 标记 appleName 字段,表示其名称总是 "Apple"。
 * 2、@FruitColor(fruitColor = FruitColor.Color.RED) 标记 appleColor 字段,表示苹果的颜色是红色。
 * 3、@FruitProvider(id = 1, name = "陕西红富士集团", address = "陕西省西安市延安路89号红富士大厦") 标记 appleProvider 字段,表示供应商的信息。
 * 4、此外,displayName 方法用于输出一条消息,表明这种水果的名字是苹果。注意这里 @FruitName、@FruitColor 和 @FruitProvider 需要对应的注解处理器支持或是在运行时进行额外的处理。
 */

/**
 * Apple类代表了一个苹果实体,包含了苹果的名称、颜色和供应商信息
 */
@Data
public class Apple {

    /**
     * 苹果的名称
     */
    @FruitName("Apple")
    private String appleName;

    /**
     * 苹果的颜色,限定为红色
     */
    @FruitColor(fruitColor = FruitColor.Color.RED)
    private String appleColor;

    /**
     * 苹果的供应商信息,包括供应商ID、名称和地址
     */
    @FruitProvider(id = 1, name = "陕西红富士集团", address = "陕西省西安市延安路89号红富士大厦")
    private String appleProvider;


    /**
     * 显示苹果的名称
     * 通过打印固定格式的字符串,来展示水果的名字
     */
    public void displayName() {
        System.out.println("水果的名字是:苹果");
    }
}
FruitRun.java
/**
 * @author: LiHao
 * @description: 输出结果
 * thinking:
 */
public class FruitRun {
    public static void main(String[] args) {
        // 调用FruitInfoUtil工具类的getFruitInfo方法,传入Apple类作为参数
        // 目的是获取关于Apple这种水果的相关信息
        FruitInfoUtil.getFruitInfo(Apple.class);
    }
}
输出结果
 水果名称:Apple
 水果颜色:RED
供应商信息: 供应商编号:1 供应商名称:陕西红富士集团 供应商地址:陕西省西安市延安路89号红富士大厦

四、总结

注解的看法与实际应用

注解在Java开发中起到了非常重要的作用,尤其是在配置和元数据处理方面。注解的使用可以使代码更加简洁、可读和易于维护

但需要注意的是,过度使用注解可能导致代码难以理解和调试,因此在实际工作中应适度使用注解,并配合良好的文档和代码规范,确保代码的可维护性。

此外,注解与反射机制紧密相关,过多依赖注解可能会导致性能问题,尤其是在处理大量注解时。因此开发者在设计系统时,需要在灵活性和性能之间找到平衡

  • 27
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值