ZetCode

Java 错误类

最后修改时间:2025 年 4 月 13 日

java.lang.Error 类代表应用程序不应尝试捕获或处理的严重问题。 错误通常源于 Java 虚拟机 (JVM),原因是运行时环境中的根本问题,例如系统资源耗尽或灾难性故障。 与异常不同,错误表示超出应用程序控制范围的情况。

错误是扩展 Throwable 的未经检查的异常。 由于它们表明了合理的应用程序无法从中恢复的严重故障,因此不应通过典型的错误处理机制来捕获或处理它们。 常见的例子包括 OutOfMemoryError(JVM 无法分配内存时发生)和 StackOverflowError(由于过度递归或深度方法调用而发生)。

错误类层次结构

Error 类是 Throwable 的直接子类,与 Exception 并列。 它有几个重要的子类,每个子类代表不同类型的严重故障。

java.lang.Throwable
    ├── java.lang.Error
    │   ├── java.lang.LinkageError
    │   ├── java.lang.ThreadDeath
    │   ├── java.lang.VirtualMachineError
    │   │   ├── java.lang.InternalError
    │   │   ├── java.lang.OutOfMemoryError
    │   │   ├── java.lang.StackOverflowError
    │   │   └── java.lang.UnknownError
    │   └── java.lang.AssertionError
    └── java.lang.Exception

理解错误类型

Error 类及其子类表示无法优雅处理的问题,这使得它们与常规异常不同。 开发人员应专注于**预防错误**,而不是捕获错误,确保适当的资源管理并避免过度递归或内存密集型操作。

OutOfMemoryError

当 JVM 无法分配对象时,会抛出 OutOfMemoryError,因为内存已耗尽。 这通常发生在堆耗尽时。 垃圾回收器无法释放足够的内存来满足请求。

Main.java
package com.zetcode;

public class Main {

    public static void main(String[] args) {
        try {
            // Allocate increasingly large arrays until memory is exhausted
            int size = Integer.MAX_VALUE;
            while (true) {
                int[] arr = new int[size];
                size *= 2;
            }
        } catch (OutOfMemoryError e) {
            System.out.println("Caught OutOfMemoryError: " + e.getMessage());
        }
    }
}

此示例通过尝试分配越来越大的数组来故意导致 OutOfMemoryError。 发生错误时,我们会捕获它并打印内存统计信息。 请注意,通常不建议在生产代码中捕获 OutOfMemoryError

StackOverflowError

当调用堆栈超过其限制时,会发生 StackOverflowError。 这通常发生在无限递归或非常深的递归中,其中每个方法调用都会消耗堆栈空间,直到没有剩余空间为止。

Main.java
package com.zetcode;

public class Main {
    public static void recursiveMethod() {
        // Infinite recursion
        recursiveMethod();
    }
    
    public static void main(String[] args) {
        try {
            recursiveMethod();
        } catch (StackOverflowError e) {
            System.out.println("Caught StackOverflowError: " + e.getMessage());
            System.out.println("Stack trace length: " + e.getStackTrace().length);
        }
    }
}

此示例演示了由无限递归引起的 StackOverflowError。 recursiveMethod 无限期地调用自身,直到堆栈空间耗尽。 我们捕获错误并打印有关堆栈跟踪的信息。

NoClassDefFoundError

当 JVM 找不到在编译时可用但在运行时不可用的类定义时,会抛出 NoClassDefFoundError。 这通常发生在 classpath 中缺少必需的类时。

Main.java
package com.zetcode;

public class Main {

    public static void main(String[] args) {
        try {
            // Attempt to use a class that exists at compile time
            // but will be removed before runtime
            Class.forName("com.example.NonExistentClass");
        } catch (ClassNotFoundException e) {
            System.out.println("ClassNotFoundException: " + e.getMessage());
        } catch (NoClassDefFoundError e) {
            System.out.println("NoClassDefFoundError: " + e.getMessage());
        }
    }
}

此示例尝试加载运行时不存在的类。 首先,它尝试使用 Class.forName,这会抛出 ClassNotFoundException。 如果该类在编译时可用但在运行时丢失,则会抛出 NoClassDefFoundError

AssertionError

当断言失败时,会抛出 AssertionError。 断言是在程序执行期间应评估为 true 的布尔表达式。 如果它们评估为 false,则会抛出 AssertionError

Main.java
package com.zetcode;

public class Main {

    public static void main(String[] args) {
        int x = 10;
        
        // Enable assertions (-ea VM option)
        assert x > 20 : "x should be greater than 20";
        
        try {
            // This will throw AssertionError if assertions are enabled
            assert false : "This assertion always fails";
        } catch (AssertionError e) {
            System.out.println("Caught AssertionError: " + e.getMessage());
        }
    }
}

此示例演示了 AssertionError。 如果 x 不大于 20,则第一个断言将失败。 第二个断言始终失败并抛出我们捕获的 AssertionError。 请注意,必须使用 -ea VM 选项启用断言。

InternalError

InternalError 指示 JVM 中发生意外的内部错误。 当 JVM 遇到它无法处理的意外情况时,会抛出此错误。 它通常表示 JVM 实现中的错误。

Main.java
package com.zetcode;

public class Main {

    public static void main(String[] args) {
        try {
            // This might throw InternalError in some JVM implementations
            // when encountering certain internal states
            throw new InternalError("Simulated JVM internal error");
        } catch (InternalError e) {
            System.out.println("Caught InternalError: " + e.getMessage());
            System.out.println("This indicates a serious JVM problem");
        }
    }
}

此示例模拟 InternalError。 实际上,当 JVM 遇到意外的内部状态时,它本身会抛出 InternalError。 应用程序通常不应抛出或捕获此错误,因为它表示 JVM 问题。

UnknownError

当 JVM 中发生未知但严重的异常时,会抛出 UnknownError。 这是一个用于不属于其他错误类别的严重问题的包罗万象的错误。

Main.java
package com.zetcode;

public class Main {

    public static void main(String[] args) {
        try {
            // Simulating an unknown serious error
            throw new UnknownError("Unknown serious JVM problem");
        } catch (UnknownError e) {
            System.out.println("Caught UnknownError: " + e.getMessage());
            System.out.println("This indicates an unspecified serious problem");
        }
    }
}

此示例演示了 UnknownError。 与 InternalError 一样,这通常由 JVM 而不是应用程序代码抛出。 它表示 JVM 中未指定的严重问题,不属于其他错误类别。

LinkageError

当类依赖项存在问题时,会发生 LinkageError。 当类在编译后发生了不兼容的更改,或者类之间存在版本冲突时,可能会发生这种情况。

Main.java
package com.zetcode;

public class Main {

    public static void main(String[] args) {
        try {
            // Simulating a linkage problem
            throw new LinkageError("Class dependency problem");
        } catch (LinkageError e) {
            System.out.println("Caught LinkageError: " + e.getMessage());
            System.out.println("This indicates a class dependency issue");
        }
    }
}

此示例演示了 LinkageError。 实际上,当类依赖项存在问题时,会发生此错误,例如不兼容的类版本。 该示例演示了如何捕获它,尽管在实际应用程序中很少这样做,因为它表示严重的配置问题。

来源

Java 错误类文档

在本文中,我们通过实际示例介绍了 Java Error 类及其常见的子类。 虽然通常不在普通应用程序代码中捕获错误,但了解它们对于调试严重的 JVM 问题非常重要。

作者

我的名字是 Jan Bodnar,我是一位专注的程序员,在该领域拥有多年的经验。 我于 2007 年开始撰写编程文章,至今已撰写了 1,400 多篇文章和八本电子书。 凭借超过八年的教学经验,我致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程