Java 异常处理

异常捕获(try–catch–finally)

  • 语法结构

    1
    2
    3
    4
    5
    6
    7
    8
    9
    try {
    // 可能抛出异常的代码
    } catch (IOException e) {
    // 针对特定异常的处理
    } catch (Exception e) {
    // 通用异常处理(放在最后)
    } finally {
    // 无论是否发生异常都会执行的代码块(如资源释放)
    }
  • 要点

    • 最多只有一个 finally,可以没有;

    • catch 顺序从最具体到最宽泛;

    • Java 7+ 支持多异常捕获:

      1
      2
      3
      catch (IOException | SQLException ex) {
      // 统一处理
      }

异常抛出(throw 与 throws)

  • throws 声明:在方法签名中标明可能向上抛出的受检异常,调用者必须处理或继续声明。

    1
    2
    3
    public void readFile(String path) throws IOException {
    // 读文件逻辑
    }
  • throw 抛出:在方法体内主动抛出一个异常对象。

    1
    2
    3
    if (input == null) {
    throw new IllegalArgumentException("输入不能为空");
    }

自定义异常

  • 继承:通常继承自 ExceptionRuntimeException

    • 受检异常(继承 Exception):调用者强制捕获或声明;
    • 运行时异常(继承 RuntimeException):可选捕获。
  • 示例

    1
    2
    3
    4
    5
    6
    7
    8
    // 受检异常
    public class MyCheckedException extends Exception {
    public MyCheckedException(String msg) { super(msg); }
    }
    // 运行时异常
    public class MyRuntimeException extends RuntimeException {
    public MyRuntimeException(String msg) { super(msg); }
    }

空指针异常(NullPointerException)

  • 触发场景:对 null 进行方法调用、字段访问或数组操作。

  • 常见避免方式

    • 显式检查:if (obj == null) { … }
    • 使用 Objects.requireNonNull(obj, "msg")
    • Java 8+ 用 Optional<T> 包装可能为空的引用。

使用断言(assert)

  • 作用:在开发/测试阶段检查假设,生产环境默认关闭。

  • 语法

    1
    2
    assert condition;
    assert condition : "失败信息";
  • 启用方式:在运行时添加 -ea-enableassertions 参数。

  • 注意:不要在关键业务逻辑或副作用代码中依赖断言。

日志框架对比与使用

JDK Logging (java.util.logging)

1
2
3
4
5
6
import java.util.logging.*;

Logger logger = Logger.getLogger("MyLogger");
logger.setLevel(Level.INFO);
logger.info("信息日志");
logger.log(Level.WARNING, "警告日志", exception);
  • 优点:无需额外依赖,简单易用。
  • 缺点:配置繁琐,性能与功能不及第三方框架。

Commons Logging (org.apache.commons.logging)

1
2
3
4
5
import org.apache.commons.logging.*;

Log log = LogFactory.getLog(MyClass.class);
log.debug("调试信息");
log.error("错误信息", e);
  • 定位:门面层,自动发现底层实现(Log4j、JDK Logging 等)。
  • 缺点:运行期查找成本较高,调试复杂。

Log4j 1.x / 2.x (org.apache.log4j / org.apache.logging.log4j)

1
2
3
4
5
6
// Log4j 2.x 示例
import org.apache.logging.log4j.*;

private static final Logger logger = LogManager.getLogger(MyClass.class);
logger.info("启动应用");
logger.error("异常发生", e);
  • 优点:功能丰富(异步、分级、过滤)、性能优秀。
  • 缺点:配置文件多样(XML、YAML、JSON),需要额外依赖。

SLF4J + Logback

1
2
3
4
5
import org.slf4j.*;

private static final Logger logger = LoggerFactory.getLogger(MyClass.class);
logger.info("处理完成");
logger.debug("变量 x={}", x);
  • SLF4J:只提供统一的 API,实际运行时绑定到具体实现。

  • Logback:由 Log4j 作者开发,默认与 SLF4J 搭配,性能优异、配置简单。

  • 优势

    • 占用内存少,启动快;
    • 支持参数化日志,避免无谓的字符串拼接开销;
    • 丰富的 Appender 与过滤控制。

日志框架选型

框架 优点 适用场景
JDK Logging 无额外依赖,轻量 小型应用、原型开发
Commons Logging 框架无侵入,统一门面 需要兼容多种底层日志库
Log4j 2 功能丰富,异步高性能 企业级项目,高并发场景
SLF4J + Logback 参数化,高性能,配置简洁 大多数新项目的首选方案