Java基础快速入门:枚举与注解 本文纲要一、枚举为什么需要枚举枚举的定义格式枚举的特点枚举的常用方法二、注解注解的优势注解概述与Java内置注解自定义注解特殊属性value自定义注解练习元注解枚举1 ) 为什么需要枚举在程序中经常需要表示一组固定的值例如四季春、夏、秋、冬。早期我们可能会这样实现publicclassSeason{publicstaticfinalintSPRING1;publicstaticfinalintSUMMER2;publicstaticfinalintAUTUMN3;publicstaticfinalintWINTER4;}这种设计存在明显缺陷代码不够简洁需要定义多个常量。仅通过常量名称区分不同的值类型安全性差。可能出现无意义的运算例如SPRING SUMMER编译器不会报错。为了更简洁、安全地表示固定的值集合Java 提供了枚举。2 ) 枚举的定义格式枚举使用enum关键字定义格式与类相似。枚举项之间用逗号分隔最后一个枚举项后可以加分号。语法格式publicenum枚举名{枚举项1,枚举项2,...;}示例// Season.javapublicenumSeason{SPRING,SUMMER,AUTUMN,WINTER;}3 ) 枚举的特点枚举的本质是一个特殊的类具备以下特点特点说明1. 所有枚举类都是Enum的子类Enum是所有枚举类型的公共基类枚举类自动继承Enum。2. 通过“枚举类名.枚举项名称”访问枚举项例如Season.SPRING。3. 每个枚举项都是该枚举类的对象Season.SPRING的类型就是Season。4. 枚举类可以定义成员变量可以在枚举中声明属性。5. 第一行必须是枚举项分号可省略但建议保留若枚举类中还有其他内容如成员变量、方法分号不能省略。6. 可以有构造方法但必须是private默认也是private枚举项会隐式调用构造方法如果未指定默认调用无参构造。7. 可以有抽象方法但每个枚举项必须重写该方法枚举项后面需要跟大括号实现抽象方法。演示代码// Season.javapublicenumSeason{SPRING(春){Overridepublicvoidshow(){System.out.println(this.name);}},SUMMER(夏){Overridepublicvoidshow(){System.out.println(this.name);}},AUTUMN(秋){Overridepublicvoidshow(){System.out.println(this.name);}},WINTER(冬){Overridepublicvoidshow(){System.out.println(this.name);}};publicStringname;// 有参构造private 可省略默认 privateprivateSeason(Stringname){this.namename;}// 抽象方法publicabstractvoidshow();}// EnumDemo.javapublicclassEnumDemo{publicstaticvoidmain(String[]args){// 特点2通过枚举类名访问枚举项System.out.println(Season.SPRING);System.out.println(Season.SUMMER);System.out.println(Season.AUTUMN);System.out.println(Season.WINTER);// 特点3每个枚举项都是该枚举类的对象SeasonspringSeason.SPRING;}}特点详解如果枚举类中定义了带参构造枚举项后必须传入参数例如SPRING(春)。如果枚举类中存在抽象方法每个枚举项都必须以匿名内部类形式重写该方法。枚举项必须位于第一行其上的注释、空格不影响但不能有其他语句。4 ) 枚举的常用方法枚举类继承自Enum因此可以直接使用以下方法方法说明String name()获取枚举项的名称。int ordinal()返回枚举项在枚举类中的索引从0开始。int compareTo(E o)比较两个枚举项返回索引差值。String toString()返回枚举项的名称与name()类似主要用于输出。static T T valueOf(ClassT type, String name)根据枚举类和名称获取对应的枚举项。static E[] values()获取所有枚举项返回数组。示例// Season.javapublicenumSeason{SPRING,SUMMER,AUTUMN,WINTER;}// EnumDemo.javapublicclassEnumDemo{publicstaticvoidmain(String[]args){// name()StringnameSeason.SPRING.name();System.out.println(name);// SPRINGSystem.out.println(-----------------------------);// ordinal()intindex1Season.SPRING.ordinal();intindex2Season.SUMMER.ordinal();intindex3Season.AUTUMN.ordinal();intindex4Season.WINTER.ordinal();System.out.println(index1);// 0System.out.println(index2);// 1System.out.println(index3);// 2System.out.println(index4);// 3System.out.println(-----------------------------);// compareTo()intresultSeason.SPRING.compareTo(Season.WINTER);System.out.println(result);// -3 (0 - 3)System.out.println(-----------------------------);// toString()StringsSeason.SPRING.toString();System.out.println(s);// SPRINGSystem.out.println(-----------------------------);// valueOf()SeasonspringEnum.valueOf(Season.class,SPRING);System.out.println(spring);System.out.println(Season.SPRINGspring);// trueSystem.out.println(-----------------------------);// values()Season[]valuesSeason.values();for(Seasonvalue:values){System.out.println(value);}}}注意toString()虽然也能返回名称但一般直接使用name()。toString()的存在意义在于打印枚举项时自动调用输出名称而非地址值。注解1 ) 注解的优势在传统的 Servlet 配置中我们通常使用 XML 文件例如web.xmlservletservlet-nameMyServlet/servlet-nameservlet-classcom.example.MyServlet/servlet-class/servletservlet-mappingservlet-nameMyServlet/servlet-nameurl-pattern/my/url-pattern/servlet-mappingXML 虽然增强了可读性但配置过多时文件会变得臃肿。注解的出现简化了配置可以直接在类上声明WebServlet(/my)publicclassMyServletextendsHttpServlet{...}优点简洁、直观、零配置。2 ) 注解概述与Java内置注解注解Annotation是对程序进行标注和解释的元数据主要用于给编译器或虚拟机提供信息。常见内置注解注解作用Override标注方法为重写父类的方法编译器会检查重写是否正确。Deprecated表示方法已过时不推荐使用。SuppressWarnings压制警告可作用于方法或类。示例代码// Fu.javapublicclassFu{publicvoidshow(){System.out.println(父类的方法);}}// Zi.javaSuppressWarnings(valueall)// 压制本类中所有警告publicclassZiextendsFu{Overridepublicvoidshow(){System.out.println(子类的方法);}Deprecatedpublicvoidmethod(){System.out.println(method.......);}publicvoidfunction2(){inta10;}SuppressWarnings(valueall)// 压制本方法中所有警告publicvoidfunction(){inta10;intb20;// 未使用的变量默认会显示灰色警告加上注解后警告消失}}注解与注释的区别注释给程序员看的解释代码逻辑。注解给编译器/虚拟机看的携带程序的特殊功能。3 ) 自定义注解自定义注解使用interface关键字格式如下publicinterface注解名{public类型 属性名()default默认值;}属性类型支持基本数据类型int、float等StringClass注解枚举以上类型的一维数组示例定义并使用自定义注解项目结构myannotation/src/com/wb/myanno2/ ├── Anno1.java ├── Anno2.java ├── AnnoDemo.java └── Season.java// Season.java枚举用于注解属性类型publicenumSeason{SPRING,SUMMER,AUTUMN,WINTER;}// Anno2.java另一个注解用于嵌套publicinterfaceAnno2{}// Anno1.java自定义注解publicinterfaceAnno1{// 基本类型inta()default23;// String类型publicStringname()defaultitheima;// Class类型publicClassclazz()defaultAnno2.class;// 注解类型publicAnno2anno()defaultAnno2;// 枚举类型publicSeasonseason()defaultSeason.SPRING;// int数组publicint[]arr()default{1,2,3,4,5};// 枚举数组publicSeason[]seasons()default{Season.SPRING,Season.SUMMER};// 特殊属性 valuepublicStringvalue();}// AnnoDemo.java使用注解// 如果注解中有属性没有默认值使用时必须赋值// Anno1(name itheima) // 完整写法Anno1(abc)// 如果只给 value 赋值可以省略 valuepublicclassAnnoDemo{}使用规则如果注解的属性未指定默认值使用时必须显式赋值。赋值的格式属性名 值多个属性用逗号分隔。4 ) 特殊属性 value当注解中定义了名为value的属性且在使用时只给该属性赋值则可以省略value直接写值。示例publicinterfaceAnno1{publicStringvalue();}Anno1(hello)// 等价于 Anno1(value hello)publicclassTest{}5 ) 自定义注解练习需求自定义一个Test注解将其标注在方法上。程序运行时自动执行所有标注了Test的方法。实现步骤定义Test注解并指定其保留策略为RUNTIME运行时可见。在目标类UseTest的某些方法上添加Test。通过反射获取UseTest的所有方法检查是否标注了Test如果是则执行。项目结构myannotation/src/com/wb/myanno3/ ├── Test.java ├── UseTest.java └── AnnoDemo.java// Test.javaimportjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;Retention(valueRetentionPolicy.RUNTIME)// 保留到运行时publicinterfaceTest{}// UseTest.javapublicclassUseTest{publicvoidshow(){System.out.println(UseTest....show....);}Testpublicvoidmethod(){System.out.println(UseTest....method....);}Testpublicvoidfunction(){System.out.println(UseTest....function....);}}// AnnoDemo.javaimportjava.lang.reflect.InvocationTargetException;importjava.lang.reflect.Method;publicclassAnnoDemo{publicstaticvoidmain(String[]args)throwsException{// 1. 获取 UseTest 类的字节码对象Class?clazzClass.forName(com.wb.myanno3.UseTest);// 2. 创建对象用于后续调用方法UseTestuseTest(UseTest)clazz.newInstance();// 3. 获取所有方法Method[]methodsclazz.getDeclaredMethods();// 4. 遍历方法检查是否有 Test 注解for(Methodmethod:methods){// isAnnotationPresent判断是否存在指定注解if(method.isAnnotationPresent(Test.class)){method.invoke(useTest);// 执行方法}}}}运行结果UseTest....method.... UseTest....function....关键点注解必须声明Retention(RetentionPolicy.RUNTIME)否则在运行时无法通过反射读取。6 ) 元注解元注解是描述注解的注解JDK 提供了四个常用元注解元注解作用Target指定注解能使用的位置类、方法、成员变量等。Retention指定注解的生命周期源码、字节码、运行时。Inherited表示注解可以被子类继承。Documented表示注解会出现在 API 文档中。示例// Anno.javaimportjava.lang.annotation.*;Target({ElementType.FIELD,ElementType.TYPE,ElementType.METHOD})// 可作用在成员变量、类、方法上Retention(RetentionPolicy.RUNTIME)// 保留到运行时//Inherited // 打开则允许子类继承该注解publicinterfaceAnno{}// Person.java父类AnnopublicclassPerson{}// Student.java子类publicclassStudentextendsPerson{publicvoidshow(){System.out.println(student.......show..........);}}// StudentDemo.java测试继承publicclassStudentDemo{publicstaticvoidmain(String[]args)throwsClassNotFoundException{Class?clazzClass.forName(com.wb.myanno4.Student);booleanresultclazz.isAnnotationPresent(Anno.class);System.out.println(result);// 若 Inherited 生效则为 true否则 false}}说明Target取值ElementType.FIELD字段、TYPE类/接口、METHOD方法等。Retention取值RetentionPolicy.SOURCE源码阶段丢弃、CLASS保留到字节码默认、RUNTIME保留到运行时可通过反射获取。Inherited仅对类上的注解有效方法、字段上的注解无法继承。总结本文系统介绍了 Java 中枚举和注解的核心概念、使用方式及常见场景适合初学者快速入门。掌握这些基础知识后可以更好地理解框架中的配置和元数据机制。