Java基础快速入门:枚举与注解
本文纲要
一、枚举
- 为什么需要枚举
- 枚举的定义格式
- 枚举的特点
- 枚举的常用方法
二、注解
- 注解的优势
- 注解概述与
Java内置注解 - 自定义注解
- 特殊属性
value - 自定义注解练习
- 元注解
枚举
1 ) 为什么需要枚举
在程序中经常需要表示一组固定的值,例如四季(春、夏、秋、冬)。早期我们可能会这样实现:
publicclassSeason{publicstaticfinalintSPRING=1;publicstaticfinalintSUMMER=2;publicstaticfinalintAUTUMN=3;publicstaticfinalintWINTER=4;}这种设计存在明显缺陷:
- 代码不够简洁,需要定义多个常量。
- 仅通过常量名称区分不同的值,类型安全性差。
- 可能出现无意义的运算(例如
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 可省略,默认 private)privateSeason(Stringname){this.name=name;}// 抽象方法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:每个枚举项都是该枚举类的对象Seasonspring=Season.SPRING;}}特点详解:
- 如果枚举类中定义了带参构造,枚举项后必须传入参数,例如
SPRING("春")。 - 如果枚举类中存在抽象方法,每个枚举项都必须以匿名内部类形式重写该方法。
- 枚举项必须位于第一行,其上的注释、空格不影响,但不能有其他语句。
4 ) 枚举的常用方法
枚举类继承自Enum,因此可以直接使用以下方法:
| 方法 | 说明 |
|---|---|
String name() | 获取枚举项的名称。 |
int ordinal() | 返回枚举项在枚举类中的索引(从0开始)。 |
int compareTo(E o) | 比较两个枚举项,返回索引差值。 |
String toString() | 返回枚举项的名称(与name()类似,主要用于输出)。 |
static <T> T valueOf(Class<T> type, String name) | 根据枚举类和名称获取对应的枚举项。 |
static E[] values() | 获取所有枚举项,返回数组。 |
示例:
// Season.javapublicenumSeason{SPRING,SUMMER,AUTUMN,WINTER;}// EnumDemo.javapublicclassEnumDemo{publicstaticvoidmain(String[]args){// name()Stringname=Season.SPRING.name();System.out.println(name);// SPRINGSystem.out.println("-----------------------------");// ordinal()intindex1=Season.SPRING.ordinal();intindex2=Season.SUMMER.ordinal();intindex3=Season.AUTUMN.ordinal();intindex4=Season.WINTER.ordinal();System.out.println(index1);// 0System.out.println(index2);// 1System.out.println(index3);// 2System.out.println(index4);// 3System.out.println("-----------------------------");// compareTo()intresult=Season.SPRING.compareTo(Season.WINTER);System.out.println(result);// -3 (0 - 3)System.out.println("-----------------------------");// toString()Strings=Season.SPRING.toString();System.out.println(s);// SPRINGSystem.out.println("-----------------------------");// valueOf()Seasonspring=Enum.valueOf(Season.class,"SPRING");System.out.println(spring);System.out.println(Season.SPRING==spring);// trueSystem.out.println("-----------------------------");// values()Season[]values=Season.values();for(Seasonvalue:values){System.out.println(value);}}}注意:toString()虽然也能返回名称,但一般直接使用name()。toString()的存在意义在于打印枚举项时自动调用,输出名称而非地址值。
注解
1 ) 注解的优势
在传统的 Servlet 配置中,我们通常使用 XML 文件,例如web.xml:
<servlet><servlet-name>MyServlet</servlet-name><servlet-class>com.example.MyServlet</servlet-class></servlet><servlet-mapping><servlet-name>MyServlet</servlet-name><url-pattern>/my</url-pattern></servlet-mapping>XML 虽然增强了可读性,但配置过多时文件会变得臃肿。
注解的出现简化了配置,可以直接在类上声明:
@WebServlet("/my")publicclassMyServletextendsHttpServlet{...}优点:简洁、直观、零配置。
2 ) 注解概述与Java内置注解
注解(Annotation)是对程序进行标注和解释的元数据,主要用于给编译器或虚拟机提供信息。
常见内置注解:
| 注解 | 作用 |
|---|---|
@Override | 标注方法为重写父类的方法,编译器会检查重写是否正确。 |
@Deprecated | 表示方法已过时,不推荐使用。 |
@SuppressWarnings | 压制警告,可作用于方法或类。 |
示例代码:
// Fu.javapublicclassFu{publicvoidshow(){System.out.println("父类的方法");}}// Zi.java@SuppressWarnings(value="all")// 压制本类中所有警告publicclassZiextendsFu{@Overridepublicvoidshow(){System.out.println("子类的方法");}@Deprecatedpublicvoidmethod(){System.out.println("method.......");}publicvoidfunction2(){inta=10;}@SuppressWarnings(value="all")// 压制本方法中所有警告publicvoidfunction(){inta=10;intb=20;// 未使用的变量默认会显示灰色警告,加上注解后警告消失}}注解与注释的区别:
- 注释:给程序员看的,解释代码逻辑。
- 注解:给编译器/虚拟机看的,携带程序的特殊功能。
3 ) 自定义注解
自定义注解使用@interface关键字,格式如下:
public@interface注解名{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(另一个注解,用于嵌套)public@interfaceAnno2{}// Anno1.java(自定义注解)public@interfaceAnno1{// 基本类型inta()default23;// String类型publicStringname()default"itheima";// Class类型publicClassclazz()defaultAnno2.class;// 注解类型publicAnno2anno()default@Anno2;// 枚举类型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 赋值,可以省略 "value="publicclassAnnoDemo{}使用规则:
- 如果注解的属性未指定默认值,使用时必须显式赋值。
- 赋值的格式:
属性名 = 值,多个属性用逗号分隔。
4 ) 特殊属性 value
当注解中定义了名为value的属性,且在使用时只给该属性赋值,则可以省略value=,直接写值。
示例:
public@interfaceAnno1{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(value=RetentionPolicy.RUNTIME)// 保留到运行时public@interfaceTest{}// 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<?>clazz=Class.forName("com.wb.myanno3.UseTest");// 2. 创建对象(用于后续调用方法)UseTestuseTest=(UseTest)clazz.newInstance();// 3. 获取所有方法Method[]methods=clazz.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 // 打开则允许子类继承该注解public@interfaceAnno{}// Person.java(父类)@AnnopublicclassPerson{}// Student.java(子类)publicclassStudentextendsPerson{publicvoidshow(){System.out.println("student.......show..........");}}// StudentDemo.java(测试继承)publicclassStudentDemo{publicstaticvoidmain(String[]args)throwsClassNotFoundException{Class<?>clazz=Class.forName("com.wb.myanno4.Student");booleanresult=clazz.isAnnotationPresent(Anno.class);System.out.println(result);// 若 @Inherited 生效则为 true,否则 false}}说明:
@Target取值ElementType.FIELD(字段)、TYPE(类/接口)、METHOD(方法)等。@Retention取值RetentionPolicy.SOURCE(源码阶段丢弃)、CLASS(保留到字节码,默认)、RUNTIME(保留到运行时,可通过反射获取)。@Inherited仅对类上的注解有效,方法、字段上的注解无法继承。
总结
本文系统介绍了 Java 中枚举和注解的核心概念、使用方式及常见场景,适合初学者快速入门。掌握这些基础知识后,可以更好地理解框架中的配置和元数据机制。
