Java基础-枚举类与注解

1|0枚举类&注解

1|1一、枚举类的使用

1|0枚举类是什么?

枚举类即是对象确定有限个的类。比如

  • 星期一、星期二、星期三、...
  • 男、女
  • 春、夏、秋、冬

这些都可以使用枚举类进行创建。

当我们需要定义带有相同关系的一组常量时,建议使用枚举类。

1|0枚举类的实现?

  • 在JDK 1.5 之前我们需要自定义枚举类
  • 而在JDK 1.5 之后新添的enum关键字,用于定义枚举类。

若枚举类只有一个对象,则可以作为一种单例模式的实现方式。

1|0枚举类的属性?

  • 枚举类对象的属性不应允许被改动,所以应该使用private final进行修饰。
  • 枚举类的使用private final修饰的属性应该在构造器中为其赋值。
  • 若枚举类显式的定义了带参数的构造器, 则在列出枚举值时也必须对应的传入参数

1|0自定义枚举类

自定义枚举类中有几个需要注意的点:

  1. 私有化的构造器,因为枚举类的对象个数是确定且有限的,即不允许在类的外部创建其他对象。
  2. 在类的内部创建枚举类的实例。私有化的构造器只允许在类内部创建枚举类实例。同时为了保证外部不能修改其枚举类实例,需要使用public static final
  3. 对象如果有实例变量,应该声明为private final,并在构造器中初始化。
public class SeasonTest{ public static void main(String[] args){ //自定义枚举类 Season spring = Season.SPRING; System.out.println(spring.getSeasonName()); } } class Season{ /** * 声明Season对象的属性,使用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 */ public static final Season SPRING = new Season("春天","穿暖花开"); public static final Season SUMMER = new Season("夏天","夏日炎炎"); public static final Season AUTUMN = new Season("秋天","秋高气爽"); public static final Season WINTER = new Season("冬天","冰天雪地"); /** * 获取枚举类对象的属性 */ public String getSeasonDesc() { return seasonDesc; } public String getSeasonName() { return seasonName; } /** * 提供tostring方法 */ @Override public String toString() { return "Season{" + "seasonName='" + seasonName + '\'' + ", seasonDesc='" + seasonDesc + '\'' + '}'; } }

1|0使用enum定义枚举类

  • 使用说明
    • 使用enum 定义的枚举类默认继承了java.lang.Enum类,因此不能再继承其他类
    • 枚举类的构造器只能使用 private 权限修饰符
    • 枚举类的所有实例必须在枚举类中显示列出(,分隔,;结尾)。列出的实例系统会自动添加public static final 修饰。
    • 必须在枚举类的第一行声明枚举类变量。
  • JDK 1.5 中可以在switch表达时中使用Enum定义的枚举类的对象作为表达式。case子句可以直接使用枚举值的名字,无需添加枚举类作为限定。
public class SpringTest1 { public static void main(String[] args) { //使用enum关键字创建枚举类 Season1 summer = Season1.SUMMER; System.out.println(summer); //根据获得其父类,可得知:enum定义的枚举类默认继承于java.lang.Enum类 System.out.println(Season1.class.getSuperclass()); } } enum Season1{ /** * 提供当前枚举类的多个对象 : public static final * 而且必须在枚举类的第一行声明枚举类变量。 */ SPRING("春天","穿暖花开"), SUMMER("夏天","夏日炎炎"), AUTUMN("秋天","秋高气爽"), WINTER("冬天","冰天雪地"); /** * 声明Season对象的属性,使用private final修饰 */ private final String seasonName; private final String seasonDesc; /** * 构造器必须设置为私有的,因为枚举类的对象确定的,所以不能由外部调用构造器来创建对象 * 并为对象属性初始化 */ private Season1(String seasonName, String seasonDesc){ this.seasonName = seasonName; this.seasonDesc = seasonDesc; } /** * 获取枚举类对象的属性 */ public String getSeasonDesc() { return seasonDesc; } public String getSeasonName() { return seasonName; } }

1|0枚举类主要方法

  • values():返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。
  • valueOf(String str):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException
  • toString():返回当前枚举类对象常量的名称
public class TestEnumMethod{ public static void main(String[] args){ //自定义枚举类 Season1 spring = Season1.SPRING; //输出:春天 System.out.println(spring.getSeasonName()); Season1[] values = Season1.values(); //输出:SPRING SUMMER AUTUMN WINTER for (Season1 value : values) { System.out.println(value); } Thread.State[] values1 = Thread.State.values(); //遍历出线程状态枚举类的所有枚举值 for (Thread.State value : values1){ System.out.println(value); } //返回枚举类中对象名是objName的对象 Season1 winter = Season1.valueOf("WINTER"); //如果没有找到,则抛出异常IllegalArgumentException //Season1 winter1 = Season1.valueOf("WINTER1"); System.out.println(winter); //Enum类自动重写了toString方法,方法中return name; //输出:SPRING System.out.println(spring.toString()) } }

1|0实现接口的枚举类

  • 枚举类和普通类一样,枚举类可以实现一个或多个接口。
  • 若每个枚举值在调用实现的接口方法呈现相同的行为方式,则只要统一实现该方法即可。
  • 若呈现出不同的行为方式,则可以让每个枚举值分别来实现该方法。

1|0情况一:实现接口,在enum类中实现接口

interface Info { //显示对象的名字 void show(); } enum Season implements Info { //多个对象之间用逗号,最后一个对象用分号结束 SPRING("SPRING", "春天"), SUMMER("SUMMER", "夏天"), AUTUMN("AUTUMN", "秋天"), WINTER("WINTER", "冬天"); //1. 声明Seasons对象的属性 private final String SeasonName; private final String SeasonDescrp; //2. 私有化类的构造器, 并给对象赋值初始化 private Season(String SeasonName, String SeasonDescrp) { this.SeasonDescrp = SeasonDescrp; this.SeasonName = SeasonName; } //实现了接口方法 @Override public void show() { System.out.println("这是一个季节"); } }

1|0情况二:让枚举类的对象,分别实现接口中的方法

interface Info { //显示对象的名字 void show(); } enum Season implements Info { //多个对象之间用逗号,最后一个对象用分号结束 SPRING("SPRING", "春天"){ @Override public void show() { //春天的方法 System.out.println("春天"); } }, SUMMER("SUMMER", "夏天") { @Override public void show() { System.out.println("夏天"); } }, AUTUMN("AUTUMN", "秋天") { @Override public void show() { System.out.println("秋天"); } }, WINTER("WINTER", "冬天") { @Override public void show() { System.out.println("冬天"); } }; //1. 声明Seasons对象的属性 private final String SeasonName; private final String SeasonDescrp; //2. 私有化类的构造器, 并给对象赋值初始化 private Season(String SeasonName, String SeasonDescrp) { this.SeasonDescrp = SeasonDescrp; this.SeasonName = SeasonName; } }

1|2二、注解的使用

1|0概述

  • 从 JDK 5.0 开始, Java 增加了对元数据(MetaData) 的支持, 也就是 Annotation(注解)
  • Annotation 其实就是代码里的特殊标记, 这些标记可以在编译, 类加载, 运行时被读取, 并执行相应的处理。通过使用 Annotation, 程序员可以在不改变原有逻辑的情况下, 在源文件中嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。
  • Annotation 可以像修饰符一样被使用, 可用于修饰包,类, 构造器, 方法, 成员变量, 参数, 局部变量的声明, 这些信息被保存在 Annotation 的 “name=value” 对中。
  • 在JavaSE中,注解的使用目的比较简单,例如标记过时的功能, 忽略警告等。在 JavaEE/Android 中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗代码和XML配置等。
  • 未来的开发模式都是基于注解的,JPA是基于注解的,Spring2.5以上都是基于注解的,Hibernate3.x以后也是基于注解的,现在的 Struts2有一部分也是基于注解的了,注解是一种趋势,一定程度上可以说:框架 = 注解 + 反射 + 设计模式。

1|0常见的Annotation示例

示例一:在 javadoc 生成文档中使用注解

类似于:@author、@version、@see、@since、@param等等

在生成文档中可以显示出来。

示例二:在编译时进行格式检查(JDK内置的三个基本注解)

  • @Override: 限定重写父类方法, 该注解只能用于方法
  • @Deprecated: 用于表示所修饰的元素(类, 方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择
  • @SuppressWarnings: 抑制编译器警告
class Person { public void walk() { System.out.println("人走路"); } //下面的用于标记,这个方法已经过时了 @Deprecated public void eat() { System.out.println("人吃饭"); } } interface Info { void show(); } class Student extends Person implements Info { //标记用于重写父类中的方法,以及实现接口中的方法 @Override public void walk() { System.out.println("学生走路"); } @Override public void show() { System.out.println("我是学生"); } public static main(String[] args) { //在代码中,创建的变量未使用会有警告Waring //这里的unused就是表示忽略,没使用这个警告的 @SuppressWarnings("unused") int num = 100; //System.out.println(num); //下面的这种"rawtypes"适用于使用泛型的时候并没有选取类型的警告 @SuppressWarnings({"unused", "rawtypes"}) ArrayList list = new ArrayList(); } }

示例三:跟踪代码依赖性,实现替代配置文件功能

Servlet3.0提供了注解(annotation),使得不再需要在web.xml文件中进行Servlet的部署.

image-20200226121316865

1|0自定义注解

  1. 自定义新注解使用 @interface 关键字,自动继承java.lang.annotation.Annotation接口
  2. 定义 Annotation 成员变量时,以 无参数方法 的形式来声明。
  3. 只有一个参数成员,建议使用value命名,指定默认值使用default关键字
  4. 如果注解没有成员表明是一个标识作用
  5. 使用注解的时候,需要设定一个值,如果已经有了默认值,那么,就可以不用手写默认值了。
  6. 自定义的注解,需要配上信息处理流程(使用反射)才有意义。
  7. 通常会指明两个元注解,@Retention@Target
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE,TYPE_PARAMETER,TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { //可以使用default设置默认值 String value() default "hello"; }

1|0元注解

  • JDK中的元注解本质上就是:修饰注解的注解
  • JDK 1.5 提供了4中标准的元注解分别是
    • Retention:指定所修饰的Annotation的声明周期
      • 其中包含了一个RetentionPolicy类型的成员变量:SOURCE/CLASS(默认)/RUNTIME
      • 只有声明为runtime声明周期的注解,才能通过反射获取。
    • Target:用于修饰 Annotation 定义, 用于指定被修饰的 Annotation 能用于修饰哪些程序元素。
    • Documented:用于指定被该元 Annotation 修饰的 Annotation 类将被 javadoc 工具提取成文档。默认情况下,javadoc是不包括注解的。
      • 定义为Documented的注解必须设置Retention值为RUNTIME。
    • Inherited:被他修饰的注解将具有继承性,即:父类有该注解,则子类也拥有该注解.

1|0利用反射获取注解信息

1|0JDK8中注解新特性

Java 8对注解处理提供了两点改进:可重复的注解及可用于类型的注解。

此外, 反射也得到了加强,在Java8中能够得到方法参数的名称。这会简化标注在方法参数上的注解

1|0可重复注解

在JDK 8 之前如果想使用重复注解时:

public @interface MyAnnotations { MyAnnotation[] value(); } @interface MyAnnotation { String value() default "hello"; }
/** * jdk8之前的写法 */ @MyAnnotations( value = {@MyAnnotation(value = "hi"),@MyAnnotation(value = "abc")} ) class Person{ //..... }

而在JDK 8之后,我们可以使用@Repeatable注解来声明可重复注解。

@Repeatable的成员值为MyAnnotation.class

@Inherited @Retention(RetentionPolicy.RUNTIME) @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE}) public @interface MyAnnotations { MyAnnotation[] value(); } @Inherited //该注解声明为可重复注解 @Repeatable(value = MyAnnotations.class) @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE}) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation { String value() default "hello"; }
//JDK 8之后的写法 @MyAnnotation("Hello") @MyAnnotation("hi") class Person{ //.... }
  1. MyAnnotation注解和MyAnnotations注解的@Retention和@Targer注解相同,由于生命周期、作用元素的不同可能会导致可重复注解失效。
  • 生命周期MyAnnotations注解不能比MyAnnotation短,因为MyAnnotation注解使用了MyAnnotation.class作为值
  • 作用元素部位也应相同,若MyAnnotation可以修饰该结构,但MyAnnotations则不一定可以修饰,也会导致报错/
  1. @Inherited注解也应相同,否则会报错,MyAnnotation时@Inherited,而包含MyAnnotation的MyAnnotations注解则不是@Inhertied。

  2. 通过上下的比较,可以发现,JDK8的新特性是对原来注解嵌套的简化。

1|0类型注解

在Java 8之前,注解只能是在声明的地方所使用,Java8开始,注解可以应用 在任何地方。

  • ElementType.TYPE_PARAMETER:表示该注解能写在类型变量的声明语 句中(如:泛型声明)。
  • ElementType.TYPE_USE:表示该注解能写在使用类型的任何语句中。
class Generic<@MyAnnotation T>{ public void show() throws @MyAnnotation RuntimeException{ List<@MyAnnotation String> list = new ArrayList<String>(); int num = (@MyAnnotation int) 10L; } } //TYPE_PARAMETER TYPE_USE @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE,TYPE_PARAMETER,TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation { String value() default "hello"; }

__EOF__

本文作者SalemG
本文链接https://www.cnblogs.com/guosiliang/p/13546825.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   SalemG  阅读(108)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示