前言:
程序编码人员难免会遇到编程错误,但应采取必要措施避免错误发生,或能在出现错误时快速定位并修正。本章系统介绍Java程序错误的分类、异常处理机制、自定义异常、程序调试技巧,并通过综合案例演示如何优雅地处理异常。
一、程序错误分类
程序错误大致分为三类:
- 编译错误(静态错误):拼写错误或未正确导入类导致,IDEA通常能即时提示并快速修正。
- 程序异常(运行时异常) :程序运行过程中因考虑不周导致的异常终止,Java提供try-catch-finally机制处理。
- 程序运行逻辑错误:运行结果与预期不符,多为算法设计错误,需重新审视算法并修正。
二、Java异常及其处理入门
2.1 程序异常现象举例
以下代码从键盘输入两个整数并相除,当第二个数为零时会抛出ArithmeticException:
java代码演示:
java
package org.example;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个整数:");
int n1 = sc.nextInt();
System.out.println("请再输入一个整数:");
int n2 = sc.nextInt();
int n3 = n1 / n2; // 若n2为0,此处抛出异常
System.out.println(n1 + "/" + n2 + "=" + n3);
}
}
输出结果:

解决方法:在除法前检查除数是否为0,或使用异常处理机制。
Java代码演示:
java
package org.example;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个整数:");
int n1 = sc.nextInt();
System.out.println("请再输入一个整数:");
int n2 = sc.nextInt();
if (n2 == 0) System.exit(1); //如果除数为0,则退出程序从而避免异常发生
int n3 = n1 / n2;
System.out.println(n1 + " / " + n2 + " = " + n3);
}
}
2.2 Java异常处理入门
Java提供try...catch...finally机制处理运行时异常,基本语法如下:
java代码演示:
java
try {
// 可能出现异常的代码
} catch (异常类型1 变量) {
// 对异常类型1的处理代码
} catch (异常类型2 变量) {
// 对异常类型2的处理代码
} ...
finally {
// 无论是否发生异常都会执行的代码
}
改进后的除法示例:
java
package org.example;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个整数:");
int n1 = sc.nextInt();
System.out.println("请再输入一个整数:");
int n2 = sc.nextInt();
try {
int n3 = n1 / n2;
System.out.println(n1 + "/" + n2 + "=" + n3);
} catch (ArithmeticException e) {
System.out.println("发生了程序异常,异常原因如下:" + e.getMessage());
} finally {
System.out.println("程序运行结束!");
}
}
}
三、深入Java异常及异常处理
Java异常类构成了一个庞大的体系。异常类的顶端是Throwable类,典型异常类及其继承关系如图:

Java异常类体系以Throwable 为顶层,主要分支为Error 和Exception。
3.1 Error类
- JDK文档对Error类是这样解释的:Error类是Trowable的子类,它表示程序运行过程中出现了严重错误,并且应用程序不应该捕获它。
- 从这段描述可知:Error是程序无法处理的错误,表示Java虚拟机在运行应用程序时发生了严重问题。大多数Error表示代码运行时Java 虚拟机出现的问题,一般来说与代码编写者执行的操作无关,但是与程序对计算机资源的使用或占用有关。
- 解决Java程序运行时出现Error异常的有效途径就是仔细检查程序代码,减小程序对计算机资源的占用,或者补充缺失的资源。
总结:Error 类表示程序运行过程中的严重错误,应用程序不应捕获它。通常与JVM资源占用有关,如OutOfMemoryError、StackOverflowError。解决方法是优化程序算法,减少资源消耗。
3.2 Exception类
Exception 类是程序可以捕获并需要适当处理的所有异常的祖先。常见子类包括RuntimeException、IOException、ArithmeticException、NullPointerException、IndexOutOfBoundsException等。
根据程序中一个异常是否被Java编译器强制要求捕获并处理,可以进一步将Exception类及其子类划分为非检查性异常和检查性异常,习惯上称为unchecked exception和checked exception。
3.3 非检查性异常(Unchecked Exception)
又称运行时异常 ,编译器不强制捕获。主要包括Error类及其子类、RuntimeException类及其子类。常见的有:
NullPointerExceptionIndexOutOfBoundsExceptionArithmeticExceptionClassCastException
根本原因:编码疏漏。最好的解决办法是检查并修正代码缺陷。
3.4 检查性异常(Checked Exception)
又称非运行时异常 ,编译器强制要求处理,否则无法通过编译。主要包括Exception子类中除RuntimeException及其子类以外的所有异常,如:
IOExceptionSQLExceptionClassNotFoundException
示例:使用Class.forName()时必须处理ClassNotFoundException:



java代码演示:
java
package org.example;
public class Main {
public static void main(String[] args) {
try {
Class.forName("com.ttt.exception.Abc");
} catch (ClassNotFoundException e) {
System.out.println("不能加载需要的类");
}
}
}
3.5 Java异常处理规则
处理Java异常的方式是采用try...catch...finally机制:将可能出现异常的代码放置在try代码块中,并使用catch捕获所有可能出现的异常并进行处理,最后,有选择的,可以使用finally代码块进行清理工作。
注意事项:
- try块:可放置任何语句,包括可能产生异常的语句。
- catch顺序 :捕获的异常必须从小到大排列,即子类异常在前,父类异常在后。
- 异常传播 :在catch块中可通过
throw将异常抛给方法的直接调用者。
3.6 自定义异常
当内置异常类无法满足需求时,可继承Exception或其子类创建自定义异常。
java代码演示:
java
package com.ttt.exception;
public class MyException extends Exception {
private final String message;
private final ExceptionType et;
public MyException(String message, ExceptionType et) {
this.message = message;
this.et = et;
}
@Override
public String getMessage() {
return message;
}
public ExceptionType getEt() {
return et;
}
@Override
public String toString() {
return "MyException{" +
"message='" + message + '\'' +
", et=" + et +
'}';
}
}
配套枚举类:
java代码演示:
java
package com.ttt.exception;
public enum ExceptionType {
Age,
Salary
}
四、案例:优雅地处理程序异常
4.1 案例任务
定义Worker类,包含属性:name、age、salary、company。该类只有private 构造方法,通过public static工厂方法创建对象。构造方法中对参数进行检查:
- 年龄必须在18~60 之间,否则抛出
MyException,类型为ExceptionType.Age - 薪金必须在0~100000 之间,否则抛出
MyException,类型为ExceptionType.Salary
4.2 案例实施
Worker类代码:
java
package com.ttt.exception;
import java.util.Scanner;
/**
* Worker 类:表示一个员工对象,包含姓名、年龄、薪金和公司信息。
* 通过静态工厂方法 getWorkerFromInput() 从控制台获取用户输入来创建实例。
*/
public class Worker {
private String name; // 员工姓名
private int age; // 员工年龄
private float salary; // 每月薪金
private String company; // 所属公司名称
// 全局共享的 Scanner 对象,用于从控制台读取用户输入
private static Scanner sc = new Scanner(System.in);
/**
* 私有构造方法,用于创建 Worker 对象。
* 在创建时会对年龄和薪金进行合法性校验,不合法则抛出自定义异常。
*
* @param name 员工姓名
* @param age 员工年龄(合法范围:18 ~ 60)
* @param salary 每月薪金(合法范围:0 ~ 1000000)
* @param company 所属公司名称
* @throws MyException 当年龄或薪金不在合法范围内时抛出,携带对应的异常类型
*/
private Worker(String name, int age, float salary, String company) throws MyException {
// 校验年龄是否在 18~60 的合法范围内
if (age < 18 || age > 60) {
throw new MyException("年龄错误", ExceptionType.Age);
}
// 校验薪金是否在 0~1000000 的合法范围内
if (salary < 0 || salary > 1000000) {
throw new MyException("薪金错误", ExceptionType.Salary);
}
this.name = name;
this.age = age;
this.salary = salary;
this.company = company;
}
/**
* 静态工厂方法:从控制台读取用户输入,创建并返回一个 Worker 对象。
* 依次读取姓名、年龄、薪金、公司名称,若姓名或公司名称为空则返回 null。
* 若构造过程中抛出 MyException,则打印失败信息并返回 null。
*
* @return 创建成功的 Worker 对象,若输入无效或校验失败则返回 null
* @throws MyException 声明可能抛出 MyException(实际在内部已被 catch 捕获)
*/
public static Worker getWorkerFromInput() throws MyException {
// 读取员工姓名
System.out.print("请输入姓名:");
String n = sc.nextLine();
// 读取员工年龄
System.out.print("请输入年龄:");
int a = sc.nextInt();
// 读取每月薪金
System.out.print("请输入每月薪金:");
float sa = sc.nextFloat();
// 清除 nextFloat() 后缓冲区中残留的换行符,避免影响后续 nextLine() 读取
sc.nextLine();
// 读取公司名称
System.out.print("请输入公司名称:");
String co = sc.nextLine();
// 若姓名或公司名称为空白字符串,视为无效输入,直接返回 null
if (n.isBlank() || co.isBlank()) {
return null;
}
try {
// 调用私有构造方法创建 Worker 对象
return new Worker(n, a, sa, co);
} catch (MyException e) {
// 捕获年龄或薪金校验失败抛出的自定义异常,打印失败原因
System.out.println("创建Worker对象失败!");
System.out.print("失败原因:" + e.getMessage());
}
return null;
}
/**
* 重写 toString 方法,返回 Worker 对象的字符串描述。
*
* @return 包含姓名、年龄、薪金、公司信息的格式化字符串
*/
@Override
public String toString() {
return "Worker{" +
"name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
", company='" + company + '\'' +
'}';
}
}
Main测试类:
java
package org.example;
import com.ttt.exception.Worker;
public class Main {
public static void main(String[] args) {
Worker w = Worker.getWorkerFromInput();
if (w == null) {
System.out.println("不能创建Worker对象");
return;
}
System.out.println(w.toString());
}
}
运行示例:
java
请输入姓名:Gates
请输入年龄:34
请输入每月薪金:6000
请输入公司名称:TTT
Worker{name='Gates', age=34, salary=6000.0, company='TTT'}
当输入公司名称为空时:
java
请输入姓名:Gates
请输入年龄:34
请输入每月薪金:6000
请输入公司名称:
不能创建Worker对象
五、在IDEA中调试Java程序
调试程序分为四步:
- 设置断点:在代码行左侧单击,出现红色圆点。
- 以Debug模式运行 :点击IDE右上角的Debug按钮。
- 逐步执行并观察变量 :使用
Step Over(F8)逐行执行,查看变量值变化。 - 发现并修正缺陷:根据观察结果修改代码,重复上述步骤直至程序正确。




六、Java异常及程序调试最佳实践
- Error类异常:虽由JVM引起,但本质是程序算法缺陷(如内存泄漏)。应重新审视算法,减少资源占用。
- 检查性异常 :编译器强制处理,相对容易应对。务必在
catch中给出有意义的处理逻辑,而非仅打印堆栈。 - 非检查性异常:需仔细审视每行代码可能引发的运行时异常,逐一处理。鲁棒程序中,业务代码仅占30%~40%,异常处理代码占60%~70%。
- 程序调试:是发现并修正程序错误的重要技术手段,尤其适用于逻辑错误和隐蔽的运行时异常。
七、练习:将从键盘输入的字符串转换为浮点数
编写Java程序,接收键盘输入的任意字符串,将其转换为浮点数并输出。要求处理各种异常,例如:
- 输入
"1234.5"→ 输出1234.5 - 输入
"X1234.5hello"→ 捕获异常并给出友好提示
提示 :可使用Float.parseFloat()或Double.parseDouble(),并捕获NumberFormatException。