Java注解与枚举类

本文详细介绍了Java中的枚举类和注解,包括如何自定义枚举类,使用Enum关键字,以及JDK内置和自定义注解的使用方法,展示了注解在数据验证和约束中的应用,如SpringAOP和自定义ConstraintValidator的实现。
摘要由CSDN通过智能技术生成


Java注解与枚举类

枚举类

简介

枚举类是一种特殊的类,类对象只有有限个,确定的,我们称此类为枚举类我们在需要定义一组常量时用枚举类。
枚举类只有 一个对象时,则可以作为单例模式的实现方式
jdk5.0之后可以使用Enum关键字定义,也可以自定义类作为枚举类
下面我们先通过class构建枚举类来理解枚举类

自定义枚举类

class Season{
    //声明对象属性,final,private修饰,这也说明了枚举类的属性都是私有final的
    private final String seasonName;
    private final String seasonDesc;
//私有化构造器并给对象赋值,说明了枚举类构造方法私有
    private Season(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }
//提供当前枚举类的多个对象,说明了枚举类对象都是静态不可修改的
    public static final Season spring=new Season("春天","1");
    public static final Season summer=new Season("夏天","2");
    public static final Season autumn=new Season("秋天","3");
    public static final Season winter=new Season("冬天","4");
//获取对象属性

    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDesc() {
        return seasonDesc;
    }

    @Override
    public String toString() {
        return "Season{" +
                "seasonName='" + seasonName + '\'' +
                ", seasonDesc='" + seasonDesc + '\'' +
                '}';
    }

}

从代码中可以得知枚举类的特性
枚举类的属性都是私有final的
枚举类构造方法私有
枚举类对象都是静态不可修改的

使用Enum自定义枚举类

使用Enum写枚举类时可省略private,final等修饰词

public enum timeT {
 Morning("早","早上"),
 Afternoon("午","中午"),
Night("晚","晚上");
 //有明确属性可以加属性类型,没有也可以直接写属性名
 String time;
String  desc;
 // 也可以这么写:String time,
//   desc;
 timeT(String time, String desc) {
  this.time = time;
  this.desc = desc;
 }
 public String getTime() {
  return time;
 }
 public String getDesc() {
  return desc;
 }
}

枚举类中上面的三个是类对象,每个对象两个String类型的属性,并包含了构造方法和get方法
当枚举类属性类型不确定时可以不写,比如 Thread.State枚举类:

枚举类常用方法

枚举类的常用方法有以下三个:
toString()返回变量名的String类型的变量名
values()返回枚举类所有对象数组
valueOf()输入String类型枚举类对象名字,返回枚举类对象,没有就抛参数异常

public class test {
    public static void main(String[] args) {
        System.out.println(timeT.Afternoon);
        //toString()返回变量名的String类型的变量名
        System.out.println(timeT.Afternoon.toString());
        System.out.println(timeT.Night);
        System.out.println(timeT.Night.time);
        //values()返回枚举类所有对象数组
        timeT[] values = timeT.values();
        for (timeT value : values) {
            System.out.println(value);
        }
        //valueOf()输入String类型枚举类对象名字,返回枚举类对象,没有就抛参数异常
        timeT morning = timeT.valueOf("Morning");
        System.out.println(morning.toString());
    }
}

枚举类实现接口

Enum实现接口,和类一样 implements接口,重写方法即可,重写的方法可以被每个枚举类的对象调用,如果需要让每个对象调用的方法不一样,可以在对象后面加{}里面实现接口方法。
这里使用开发中常用的ErrorCode来举例

public interface IErrorCode {
    /**
     * 返回码
     */
    long getCode();

    /**
     * 返回信息
     */
    String getMessage();
}
public enum ResultCode implements IErrorCode {
    SUCCESS(200, "操作成功"),
    FAILED(500, "操作失败"),
    VALIDATE_FAILED(404, "参数检验失败"),
    UNAUTHORIZED(401, "暂未登录或token已经过期"),
    FORBIDDEN(403, "没有相关权限");
    private long code;
    private String message;

    private ResultCode(long code, String message) {
        this.code = code;
        this.message = message;
    }

    public long getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}

需要对特定对象自定义接口方法是只需在该对象构造的下面加{}即可

 SUCCESS(200, "操作成功"),
    FAILED(500, "操作失败")
            {
                @Override
                public long getCode()
                {
                    return 6666;
                }
            },
    VALIDATE_FAILED(404, "参数检验失败"),
    UNAUTHORIZED(401, "暂未登录或token已经过期"),
    FORBIDDEN(403, "没有相关权限");

注解

Annotation 其实就是代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过使用Annotation,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。
框架=注解+反射+设计模式
Annotation可以像修饰符一样被使用,可用于修饰包,类,构造器,方法,成员变量,参数,局部变量的声明,这些信息被保存在Annotation的“name=value”对中。

JDK内置3个基本注解

@Override:限定重写父类方法,该注解只能用于方法
@Deprecated:用于表示所修饰的元素(类,方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择
@SuppressWarnings:抑制编译器警告

自定义注解

定义Annotation注解用@interface 关键字
自定义注解自动继承了java.lang.annotation.Annotation接口,Annotation的成员变量在Annotation 定义中以无参数方法的形式来声明。其方法名和返回值定义了该成员的名字和类型。我们称为配置参数。
类型只能是八种基本数据类型、String类型、Class类型、enum类型、Annotation类型、以上所有类型的数组。
定义Annotation 的成员变量时使用default关键字设定初始值
只有一个参数成员,建议用参数名为value如果定义的注解含有配置参数,那么使用时必须指定参数值,除非它有默认值。格式是“参数名=参数值”,如果只有一个参数成员,且名称为value,可以省略“value=”
没有成员定义的Annotation称为标记;包含成员变量的Annotation称为元数据Annotation
注意:自定义注解必须配上注解的信息处理流程(反射)才有意义。
自定义注解在Java中可以通过反射机制来获取注解信息,并且在运行时可以根据注解的信息进行相应的处理。

元注解

元注解:修饰其他注解的注解
@Retention:指明所修饰注解的声明周期
RetentionPolicy.SOURCE:在源文件中有效(即源文件保留),编译器直接丢弃这种策略的注释
RetentionPolicy.CLASS:在class文件中有效(即class保留),当运行Java程序时,JVM不会保留注解。这是默认值
RetentionPolicy.RUNTIME:在运行时有效(即运行时保留),当运行Java程序时,JVM会保留注释。程序可以通过反射获取该注释。
@Target 指定修饰的对象,方法,类,属性,形参,构造器,局部变量,可写多个
以上是自定义注解常用的
@Documented:标识所修饰的注解在Javadoc解析时保留
@Inherited:可继承性,注解修饰类时,修饰父类,子类也会自动被该注解修饰

@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE,METHOD,FIELD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE,TYPE_PARAMETER})
@Repeatable(MyAnnotations.class)
public @interface MyAnnotation {

   //内部定义成员变量,default指定默认值,无默认值则必须指定,只有一个成员变量时可以不写value="",直接写""
    String value() default "hello";
    //如果自定义注解没有成员,表明是一个标识作用(有的接口没有抽象方法,也可以作为标识接口)
//注解依赖于反射,添加注解后,利用反射读注解,再确定注解作用

}

jdk8注解新特性

1.可重复注解
可重复注解:在一个类上修饰两个同一注解,新建一个注解,属性为老注解的数组,注解上修饰@Repeatable(MyAnnotation.class)
其余元注解配置一致即可
比如:

@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE,METHOD,FIELD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE,TYPE_PARAMETER})
@Repeatable(MyAnnotations.class)
public @interface MyAnnotation {

   //内部定义成员变量,default指定默认值,无默认值则必须指定,只有一个成员变量时可以不写value="",直接写""
    String value() default "hello";
    //如果自定义注解没有成员,表明是一个标识作用(有的接口没有抽象方法,也可以作为标识接口)
//注解依赖于反射,添加注解后,利用反射读注解,再确定注解作用

}
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE,METHOD,FIELD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
public @interface MyAnnotations {
  MyAnnotation[] value();
}

2.注解类型
注解类型:在注解修饰@Target中新增注解TYPE_PARAMETER可修饰泛型

@Target({TYPE,METHOD,FIELD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE,TYPE_PARAMETER})

自定义注解,校验器实现注解

自定义注解还可以配合SpringAOP或者自定义校验器实现特定功能
业务需求:设定多个时间线,时间线的开始时间必须早于结束时间
自定义注解

@Documented
@Constraint(validatedBy = EndTimeAfterStartTimeValidator.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EndTimeAfterStartTime {
    String message() default "结束时间必须晚于开始时间";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

@Constraint(validatedBy = EndTimeAfterStartTimeValidator.class)
注解是一个约束注解,并且指定了该注解的验证器,意味着在运行时,当使用了 @EndTimeAfterStartTime 注解时,会调用 EndTimeAfterStartTimeValidator 类中定义的逻辑来进行验证。
自定义验证器逻辑

public class EndTimeAfterStartTimeValidator implements ConstraintValidator<EndTimeAfterStartTime, AddTimeLineCmd> {
    @Override
    public boolean isValid(AddTimeLineCmd addTimeLineCmd, ConstraintValidatorContext constraintValidatorContext) {
        if(addTimeLineCmd.getProposalStart() != null && addTimeLineCmd.getProposalEnd() != null && addTimeLineCmd.getProposalEnd().before(addTimeLineCmd.getProposalStart())) {
            return false;
        }
        if(addTimeLineCmd.getApprovalStart() != null && addTimeLineCmd.getApprovalEnd() != null && addTimeLineCmd.getApprovalEnd().before(addTimeLineCmd.getApprovalStart())) {
            return false;
        }
        if(addTimeLineCmd.getInitialReviewStart() != null && addTimeLineCmd.getInitialReviewEnd() != null && addTimeLineCmd.getInitialReviewEnd().before(addTimeLineCmd.getInitialReviewStart())){
            return false;
        }
        if(addTimeLineCmd.getProvisionalCaseStart() != null && addTimeLineCmd.getProvisionalCaseEnd() != null && addTimeLineCmd.getProvisionalCaseEnd().before(addTimeLineCmd.getProvisionalCaseStart())) {
            return false;
        }
        if(addTimeLineCmd.getFormalCaseStart() != null && addTimeLineCmd.getFormalCaseEnd() != null && addTimeLineCmd.getFormalCaseEnd().before(addTimeLineCmd.getFormalCaseStart())){
            return false;
        }
        if(addTimeLineCmd.getUndertakeStart() != null && addTimeLineCmd.getUndertakeEnd() != null && addTimeLineCmd.getUndertakeEnd().before(addTimeLineCmd.getUndertakeStart())) {
            return false;
        }
        if(addTimeLineCmd.getFeedbackStart() != null && addTimeLineCmd.getFeedbackEnd() != null && addTimeLineCmd.getFeedbackEnd().before(addTimeLineCmd.getFeedbackStart())){
            return false;
        }
        return true;
    }
}

自定义验证器实现了ConstraintValidator接口,接口泛型(第一个是要验证的注解,第二个是要验证的参数类型)
当使用了 @EndTimeAfterStartTime 注解的地方,比如在某个类或者方法上,并传递了 AddTimeLineCmd 对象作为参数时,验证框架会将这个对象传递给自定义验证器。
验证器注入
开发环境为SpringBoot,所以需要将验证器注入IOC容器作为Bean

@Configuration
public class ValidatorConfig {
    @Bean
    public EndTimeAfterStartTimeValidator endTimeAfterStartTimeValidator(){
        return new EndTimeAfterStartTimeValidator();
    }
}

验证器返回true或False后的逻辑
在这里插入图片描述
在SpringBoot的全局异常处理器中配置该异常。当验证器返回 false 结果时,会抛出 ConstraintViolationException 异常。

package com.neu.dto.cmd;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.neu.annotation.EndTimeAfterStartTime;
import lombok.Data;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Date;

/**
 * @Author lwylwyll
 * @Date 2023/10/24
 * @Description:
 */
@Validated
@EndTimeAfterStartTime(message = "")
@Data
public class AddTimeLineCmd {
    @NotBlank
    private String session;
    @NotBlank
    private String degree;
    @NotNull
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date proposalStart;
    @NotNull
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date proposalEnd;
    @NotNull
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date approvalStart;
    @NotNull
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date approvalEnd;
    @NotNull
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date initialReviewStart;
    @NotNull
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date initialReviewEnd;
    @NotNull
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date provisionalCaseStart;
    @NotNull
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date provisionalCaseEnd;
    @NotNull
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date formalCaseStart;
    @NotNull
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date formalCaseEnd;
    @NotNull
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date undertakeStart;
    @NotNull
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date undertakeEnd;
    @NotNull
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date feedbackStart;
    @NotNull
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date feedbackEnd;
}


  • 41
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值