news 2026/4/28 23:06:07

JAVA之路(13)——枚举、注解和异常

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JAVA之路(13)——枚举、注解和异常

1. 枚举

1.1 为什么需要枚举

  • 有些业务的类需要包含固定数量的对象,普通类的实现无法限制对象的数量,可随意创建非法对象
  • 提供 set 方法,属性可被修改,无法保证只读
  • 因此需要有一种方法可以创建一种特殊的类,里面只包含有限的、固定的对象
class Season{ private String name; private String desc; public Season(String name, String desc) { this.name = name; this.desc = desc; } // getter和setter public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } } public class Enumeration01 { public static void main(String[] args) { Season spring = new Season("春天", "温暖"); Season winter = new Season("冬天", "寒冷"); // 致命问题1:可以随意创建非法对象,比如"红天" Season other = new Season("红天", "~~~"); // 致命问题2:可以随意修改属性,破坏只读特性 autumn.setDesc("非常的热.."); } }

1.2 自定义类实现枚举

  • 构造器私有化,禁止外部 new 对象
  • 移除 set 方法,保证枚举对象只读不可修改
  • 类内部创建固定的public static final对象,对外暴露
  • 可以提供 get 方法,不提供 set 方法
package com.hspedu.enum_; public class Enumeration02 { public static void main(String[] args) { // 只能通过类名获取固定对象,无法new System.out.println(Season.AUTUMN); System.out.println(Season.SPRING); } } // 自定义枚举类 class Season { private String name; private String desc; // 1. 构造器私有化:防止外部new对象 private Season(String name, String desc) { this.name = name; this.desc = desc; } // 2. 去掉set方法:防止属性被修改,保证只读 public String getName() { return name; } public String getDesc() { return desc; } // 3. 类内部创建固定的对象,用public static final修饰,对外暴露 public static final Season SPRING = new Season("春天", "温暖"); public static final Season WINTER = new Season("冬天", "寒冷"); public static final Season AUTUMN = new Season("秋天", "凉爽"); public static final Season SUMMER = new Season("夏天", "炎热"); // 重写toString,方便打印 @Override public String toString() { return "Season{" + "name='" + name + '\'' + ", desc='" + desc + '\'' + '}'; } }

1.3 enum 关键字实现枚举

JDK5.0 提供了enum关键字,简化枚举类的开发,用于表示一组固定且有限的常量。

  • 使用enum定义的枚举类,默认隐式继承Enum,且是final类(通过javap反编译可验证)
  • 传统的public static final ...简化为常量名(实参列表),本质还是调用构造器
  • 无参构造器创建枚举对象时,实参列表和小括号可以省略,例如:BOY, GIRL;
  • 多个枚举对象用,分隔,最后一个对象结尾用;
  • 枚举对象必须放在枚举类的行首
  • 因为已经继承了Enum类,Java 是单继承机制,所以enum枚举类不能再继承其他类
  • enum枚举类和普通类一样,可以实现多个接口
package com.hspedu.enum_; public class Enumeration03 { public static void main(String[] args) { System.out.println(Season2.AUTUMN); System.out.println(Season2.SUMMER); } } // 使用enum关键字定义枚举类 enum Season2 { // 1. 枚举对象必须写在类的最前面,多个用逗号分隔,分号结尾 // 2. 简化写法:常量名(实参列表) 等价于 public static final Season2 SPRING = new Season2("春天", "温暖") SPRING("春天", "温暖"), WINTER("冬天", "寒冷"), AUTUMN("秋天", "凉爽"), SUMMER("夏天", "炎热"); private String name; private String desc; // 构造器默认private,可省略不写 private Season2(String name, String desc) { this.name = name; this.desc = desc; } // getter方法,无setter public String getName() { return name; } public String getDesc() { return desc; } @Override public String toString() { return "Season2{" + "name='" + name + '\'' + ", desc='" + desc + '\'' + '}'; } }

1.4 enum 枚举的 6 个常用方法

方法名作用
toString()返回当前枚举常量的名称,子类可重写,用于返回对象属性信息
name()返回当前枚举常量的名称(定义时的常量名),子类不能重写
ordinal()返回当前枚举常量的声明次序(编号),默认从 0 开始
values()返回当前枚举类中所有的枚举常量,返回值为枚举数组
valueOf(String name)将字符串转换成枚举对象,要求字符串必须是已有的常量名,否则抛异常
compareTo(E o)比较两个枚举常量,比较的是声明编号的差值
package com.hspedu.enum_; public class EnumMethod { public static void main(String[] args) { Season2 autumn = Season2.AUTUMN; // 1. name():返回枚举常量名 System.out.println(autumn.name()); // 输出:AUTUMN // 2. ordinal():返回声明次序,从0开始(SPRING=0,WINTER=1,AUTUMN=2,SUMMER=3) System.out.println(autumn.ordinal()); // 输出:2 // 3. values():返回所有枚举常量数组 Season2[] values = Season2.values(); System.out.println("===遍历枚举常量==="); for (Season2 season : values) { System.out.println(season); } // 4. valueOf():字符串转枚举对象 Season2 autumn1 = Season2.valueOf("AUTUMN"); System.out.println("autumn1=" + autumn1); System.out.println(autumn == autumn1); // 输出:true // 5. compareTo():比较编号,AUTUMN编号2 - SUMMER编号3 = -1 System.out.println(Season2.AUTUMN.compareTo(Season2.SUMMER)); // 输出:-1 } }

2. 注解

注解(Annotation)也叫元数据(Metadata),用于修饰、解释包、类、方法、属性、构造器、局部变量等程序元素。

2.1 注解和注释的区别

特性注解注释
作用对象编译器、JVM、框架给程序员看的
生效阶段编译期 / 运行期仅源码阶段
功能可被程序读取,实现逻辑控制仅代码说明,无程序功能
语法@开头,有固定语法单行//、多行/* */、文档/** */

2.2 @Override

限定某个方法,是重写父类的方法,该注解只能用于方法

  • 写了@Override注解,编译器会编译期校验该方法是否真的重写了父类的方法,若没有构成重写,直接编译报错
  • 不写@Override,只要方法符合重写规则,仍然构成重写
  • 只能修饰方法,不能修饰类、包、属性等
  • 源码中@Target(ElementType.METHOD),说明只能修饰方法
package com.hspedu.annotation_; public class Override_ { public static void main(String[] args) { Son son = new Son(); son.fly(); } } class Father { public void fly() { System.out.println("Father fly..."); } public void say() {} } class Son extends Father { // 标记该方法是重写父类的fly方法 @Override public void fly() { System.out.println("Son fly...."); } @Override public void say() {} }

2.3 @Deprecated

标记某个程序元素(类、方法、字段、构造器等)已过时,不推荐使用,但仍然可以使用。

  • 用于版本升级的兼容过渡,提示开发者该元素有新的替代方案
  • 可以修饰类、方法、字段、包、参数等几乎所有程序元素
  • 被标记的元素,IDE 中会显示删除线
package com.hspedu.annotation_; public class Deprecated_ { public static void main(String[] args) { A a = new A(); a.hi(); // 调用过时方法,IDE会显示删除线 System.out.println(a.n1); } } // 标记类已过时 @Deprecated class A { // 标记字段已过时 @Deprecated public int n1 = 10; // 标记方法已过时 @Deprecated public void hi() { System.out.println("hi"); } }

2.4 @SuppressWarnings

抑制编译器警告,消除代码中黄色的警告提示,让代码更整洁。

  • 作用范围和放置位置相关:放在类上,抑制整个类的警告;放在方法上,抑制方法内的警告
  • 可以通过value参数指定要抑制的警告类型,常用的有:
    • all:抑制所有警告
    • unchecked:抑制未检查的泛型警告
    • rawtypes:抑制未指定泛型的 raw 类型警告
    • unused:抑制未使用变量 / 代码的警告
    • deprecation:抑制过时元素的警告
package com.hspedu.annotation_; import java.util.ArrayList; import java.util.List; // 抑制整个类的rawtypes、unchecked、unused警告 @SuppressWarnings({"rawtypes", "unchecked", "unused"}) public class SuppressWarnings_ { public static void main(String[] args) { List list = new ArrayList(); list.add("jack"); list.add("tom"); // 未使用的变量,不会报警告 int i; System.out.println(list.get(1)); } public void f1() { // 仅抑制当前方法的rawtypes警告 @SuppressWarnings({"rawtypes"}) List list = new ArrayList(); list.add("jack"); } }

2.5 元注解

元注解是修饰注解的注解,用于给自定义注解指定规则,开发中主要用于看源码,不用深入研究。

元注解作用
@Retention指定注解的保留范围,3 个取值:1.SOURCE:仅源码阶段,编译后丢弃2.CLASS:编译到 class 文件,JVM 运行时不保留(默认值)3.RUNTIME:编译到 class 文件,JVM 运行时保留,可通过反射获取
@Target指定注解可以修饰哪些程序元素(类、方法、字段等),通过ElementType枚举指定
@Documented指定该注解会被javadoc工具提取成 API 文档
@Inherited指定该注解具有继承性,父类使用了该注解,子类会自动继承

3. 异常

Java 中,程序执行过程中发生的不正常情况(非语法错误、非逻辑错误)称为异常

3.1 异常的体系结构

  • 异常分为两大类:Error(错误)Exception(异常)
  • Exception 又分为:运行时异常(程序运行时触发)和编译时异常(编译期就必须处理)
  • 运行时异常是RuntimeException及其子类,编译时异常是其他 Exception 子类
java.lang.Object └── java.lang.Throwable ├── Error(错误:JVM级别的严重问题,无法处理) │ ├── StackOverflowError(栈溢出) │ └── OutOfMemoryError(OOM,内存溢出) └── Exception(异常:可通过代码处理的一般性问题) ├── RuntimeException(运行时异常,非受检异常) │ ├── NullPointerException(空指针异常) │ ├── ArithmeticException(算术异常) │ ├── ArrayIndexOutOfBoundsException(数组下标越界) │ ├── ClassCastException(类型转换异常) │ └── NumberFormatException(数字格式异常) └── 编译时异常(受检异常,CheckedException) ├── IOException(IO异常) ├── FileNotFoundException(文件不存在异常) ├── ClassNotFoundException(类不存在异常) └── SQLException(数据库操作异常)

3.2 运行异常

  • 定义:java.lang.RuntimeException及其子类,编译器不会检查,即使不处理也能编译通过,运行时才会触发。通常是编程逻辑错误,程序员应该主动避免。
  • 特点:Java 不要求必须显式处理,默认会以throws的方式向上抛出,最终交给 JVM 处理(打印异常栈信息,终止程序)。

常见运行异常

异常类型触发场景代码案例
NullPointerException空指针异常null的对象调用方法 / 属性java String name = null; System.out.println(name.length()); // 触发空指针
ArithmeticException算术异常错误的数学运算,最常见整数除以 0java int num1 = 10; int num2 = 0; int res = num1 / num2; // 触发算术异常
ArrayIndexOutOfBoundsException数组下标越界异常使用非法索引访问数组(索引为负 / 大于等于数组长度)java int[] arr = {1,2,4}; // 数组长度3,索引最大为2 for (int i = 0; i <= arr.length; i++) { System.out.println(arr[i]); // i=3时触发越界 }
ClassCastException类型转换异常强制类型转换时,对象不是目标类型的实例java A b = new B(); // 向上转型 C c2 = (C)b; // B和C无继承关系,触发类型转换异常 class A {} class B extends A {} class C extends A {}
NumberFormatException数字格式异常字符串无法转换为对应数值类型java String name = "韩顺平教育"; int num = Integer.parseInt(name); // 非数字字符串转int,触发异常

3.3 编译异常

  • 定义:也叫受检异常(CheckedException),编译器在编译期就会检查,必须显式处理(try-catch/throws),否则代码无法编译通过。
  • 特点:通常是外部因素导致的异常(如文件不存在、网络中断),不是程序逻辑错误,必须提前处理。

常见的编译时异常

  1. IOException:操作文件 / 流时发生的 IO 异常
  2. FileNotFoundException:操作不存在的文件时触发,是 IOException 的子类
  3. ClassNotFoundException:加载类时,类不存在触发
  4. SQLException:操作数据库时发生的异常

3.4 异常处理方式

为了保证当异常发生时,不让程序直接崩溃,而是针对性处理,保证程序继续运行,Java 提供了两种处理方式:try-catch-finallythrows。

3.4.1 try-catch-finally 异常捕获

程序员在代码中捕获异常,自行处理,是最常用的异常处理方式。

基本语法

try { // 可疑代码:可能发生异常的代码 } catch (异常类型1 异常对象名) { // 捕获到异常类型1时,执行的处理逻辑 } catch (异常类型2 异常对象名) { // 捕获到异常类型2时,执行的处理逻辑 } finally { // 可选:不管try中是否发生异常,始终会执行的代码 // 通常用于释放资源:关闭文件、关闭数据库连接等 }

使用细节

  • 异常发生后的执行逻辑:异常发生后,try 块中异常位置之后的代码不会执行,直接进入匹配的 catch 块。
  • 无异常的执行逻辑:try 块中代码全部正常执行,不会进入任何 catch 块。
  • 多个 catch 的规则:可以有多个 catch 块捕获不同异常,子类异常必须写在前面,父类异常写在后面,否则编译报错。异常发生后,只会匹配第一个符合的 catch 块。
try { Person person = new Person(); person = null; System.out.println(person.getName()); // 空指针 int res = 10 / 0; // 算术异常 } catch (NullPointerException e) { // 子类异常在前 System.out.println("空指针异常"); } catch (ArithmeticException e) { // 子类异常在前 System.out.println("算术异常"); } catch (Exception e) { // 父类异常在后 System.out.println("其他异常"); }
  • finally 的执行特性:不管 try 中是否发生异常、是否有 return,finally 块始终会执行(唯一不执行的情况:System.exit(0)终止 JVM)。
  • try-finally 配合使用:可以没有 catch 块,只使用 try-finally。这种用法不会捕获异常,程序会直接崩溃,但能保证必须执行的业务逻辑(如释放资源)。
try { int res = 10 / 0; } finally { System.out.println("必须执行的代码"); } // 异常发生后,finally执行完毕,程序依然会崩溃
  • try/catch 中的 return 语句,会先计算返回值并保存临时变量,然后执行 finally 块,最后再 return,此时return的是临时变量。
  • 但是如果 finally 块中有 return 语句,会直接覆盖 try/catch 中的 return,最终返回 finally 中的值。

3.4.2 throws 异常抛出

如果一个方法执行时可能生成异常,但无法确定如何处理,该方法可以显式声明抛出异常,将异常交给方法的调用者处理,最顶级的处理者是 JVM,JVM 处理异常时会打印异常堆栈信息,并终止程序。

基本语法

// 在方法声明处,通过throws关键字声明抛出的异常类型,多个异常用逗号分隔。 // 声明方法会抛出FileNotFoundException,交给调用者处理 public static void readFile(String file) throws FileNotFoundException { // 读文件操作,可能抛出FileNotFoundException FileInputStream fis = new FileInputStream("d://aa.txt"); }

使用细节

  • 编译异常必须处理:对于编译时异常,程序中必须处理,要么try-catch,要么throws,否则编译报错。
  • 运行时异常默认处理:对于运行时异常,程序中如果没有处理,默认就是throws的方式向上抛出,无需显式声明。
  • 子类重写规则:子类重写父类的方法时,抛出的异常类型必须和父类一致,或为父类抛出异常的子类型,不能抛出父类没有声明的更大范围的异常。
  • 异常传递规则:方法 A 调用了声明抛出异常的方法 B,方法 A 要么处理这个异常,要么继续向上抛出。

3.4.3 自定义异常

当程序中出现了特定的业务错误,而该错误没有在 JDK 的 Throwable 子类中描述时,我们可以自己设计异常类,用于描述该业务错误信息。

实现步骤

  • 定义自定义异常类,继承ExceptionRuntimeException
  • 如果继承Exception,属于编译时异常,必须显式处理
  • 如果继承RuntimeException,属于运行时异常,推荐使用(可以使用默认的处理机制,更灵活)
  • 编写构造器,通过super(message)传递异常提示信息
package com.hspedu.customexception_; public class CustomException { public static void main(String[] args) { int age = 180; if(!(age >= 18 && age <= 120)) { // 手动抛出自定义异常 throw new AgeException("年龄需要在18~120 之间"); } System.out.println("你的年龄范围正确."); } } // 自定义异常:继承RuntimeException,运行时异常 class AgeException extends RuntimeException { // 构造器,接收异常信息 public AgeException(String message) { super(message); } }

3.4.4 throw 和 throws 关键字的区别

维度throwsthrow
核心意义异常处理的一种方式,声明方法会抛出的异常手动生成并抛出异常对象的关键字
位置方法声明处,跟在方法名后面方法体内部
后面跟的内容异常类型(可以多个,逗号分隔)异常对象(只能一个)
作用告诉调用者方法可能发生的异常,让调用者提前处理主动触发异常,将异常对象交给调用者
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/28 22:55:41

Qwen2.5-7B-Instruct实测:29种语言支持,打造国际化智能助手

Qwen2.5-7B-Instruct实测&#xff1a;29种语言支持&#xff0c;打造国际化智能助手 1. 引言&#xff1a;多语言大模型的新标杆 在全球化日益深入的今天&#xff0c;语言障碍仍然是信息交流的重要壁垒。Qwen2.5-7B-Instruct作为新一代多语言大模型&#xff0c;以其出色的29种语…

作者头像 李华
网站建设 2026/4/28 22:54:42

AssetRipper终极指南:Unity资源逆向工程与提取完整教程

AssetRipper终极指南&#xff1a;Unity资源逆向工程与提取完整教程 【免费下载链接】AssetRipper GUI Application to work with engine assets, asset bundles, and serialized files 项目地址: https://gitcode.com/GitHub_Trending/as/AssetRipper AssetRipper是一款…

作者头像 李华
网站建设 2026/4/28 22:50:25

智慧校园系统厂家合作模式选择指南:代理与OEM如何权衡

✅作者简介&#xff1a;合肥自友科技 &#x1f4cc;核心产品&#xff1a;智慧校园平台(包括教工管理、学工管理、教务管理、考务管理、后勤管理、德育管理、资产管理、公寓管理、实习管理、就业管理、离校管理、科研平台、档案管理、学生平台等26个子平台) 。公司所有人员均有多…

作者头像 李华