为什么允许 main() 方法声明异常?

5
"处理或声明。这就是规则。" -《Head First》 但这是一条好的规则吗?让我先举个例子:
public static void main(String[] args) throws Exception {
    m1();
}

static void m1() throws Exception{
    m2();
}

static void m2() throws Exception {
    throw new Exception();
}

m2() 抛出异常并且 m1() 调用了 m2(),这意味着它必须要么处理它,要么声明它。好的,我们来声明它。然后 main() 调用了 m1(),也需要做出相同的选择:是声明还是处理。 我再次决定将其声明,代码编译通过。

好的,它能工作,但到底是谁处理了这个异常呢?看起来似乎没有人。我知道我是一个初学者,但我不喜欢听起来像那样。是的,有些方法可以决定是声明还是处理异常,但为什么是main()呢?难道主方法不应该只是处理吗?这样就不会有任何异常“溜走”了。

我错过了什么吗?老实说,我很惊讶居然允许主方法仅仅是声明异常,尤其知道它是我们从技术上讲最后一个可以捕获异常的地方。


异常设计用于处理程序状态未知的意外情况。如果您已了解此类情况,您可以处理该异常。否则,最好让程序崩溃而不是继续使用错误的数据。 - NomadMaker
2
它会进入线程的默认异常处理程序,因为未经检查的异常也可能发生。最好在main中处理异常,但您也可以将其留给默认的异常处理程序。 - RealSkeptic
@NomadMaker,我不同意异常意味着“程序状态未知”。异常只是意味着“我无法完成所要求的操作”...通常,取例程名称并在其开头添加“我无法...”即可。例如,“ProcessFile”如果无法处理文件,则应引发异常。“ValidateLogin”如果提供了错误的密码,则不应引发异常...在这种情况下,它能够验证用户,即登录失败。但是,如果用户数据库不可用,则引发异常。 - JoelFan
没有人处理它 -> 程序崩溃。你越快捕捉,停止的代码就越少。 - azro
@NomadMaker,错误代码在最好的情况下会导致难以阅读的代码,在最坏的情况下会导致遗忘检查。这就是为什么引入了异常处理。最佳使用异常的情况不是在“靠近代码”处处理它们,也不是在“主函数中备份”,而是在中间某个地方处理。例如,处理多个项目的方法可以处理在一个项目上抛出的异常(可能通过记录日志或创建失败项目列表)并继续处理剩余的项目。 “重建确切发生的事情”并不总是要求。 - JoelFan
显示剩余3条评论
2个回答

6

谁处理了这个异常?

Java运行时处理了这个异常。

更具体地说,根据Java语言规范(JLS)第11.3节UncaughtExceptionHandler 处理了它。

如果没有能够处理异常的catch子句,那么当前线程(遇到异常的线程)将被终止。在终止之前,所有的finally子句都会被执行,并根据以下规则处理未捕获的异常:
  • 如果当前线程设置了未捕获异常处理程序,则执行该处理程序。
  • 否则,调用当前线程的父ThreadGroupuncaughtException方法。如果ThreadGroup及其父ThreadGroup没有覆盖uncaughtException,则调用默认处理程序的uncaughtException方法。
因此,默认情况下,当main()抛出一个异常(无论是未检查的还是已检查的),内置的默认“未捕获异常处理程序”将简单地将堆栈跟踪打印到System.err,就好像在调用main()之前执行了以下操作一样:
Thread.setDefaultUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler());

class DefaultUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        e.printStackTrace();
    }
}

在调用了“未捕获异常处理程序”之后,线程将被终止,就像您仅从main()方法返回一样,除非代码已经启动了仍在运行的非守护线程,程序才会结束。

1
从技术上讲,堆栈跟踪应该输出到 System.err 而不是 System.out。 - VGR
@VGR 我在打字的时候其实也在想这个问题,但我的手指还是打出了 out。该死的肌肉记忆或者叫什么来着?习惯吗?叹气 - Andreas

0
应用程序可以包含多个具有主方法的类。在这种情况下,应用程序应该声明一个清单来知道哪个主方法是入口点(首先调用的方法)。主方法可以被从另一个方法或另一个主方法作为静态方法调用,并且可以抛出任何异常。如果您在入口点至少不捕获异常,则异常从应用程序返回到Java虚拟机,然后机器决定如何处理异常。通常,JVM会打印错误消息并返回操作系统以外的其他返回值。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接