主方法为什么允许声明异常?

huangapple go评论68阅读模式
英文:

Why is main() method allowed to declare exceptions?

问题

> "处理或声明。这就是法律。" - 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() 呢?主方法不应该只负责处理吗?这样的话,异常就不会“溜走”。

我是不是漏掉了什么?说实话,我很惊讶原来主方法只需要声明异常就可以了,尤其是考虑到从技术上讲这是我们可能捕获某些东西的最后机会

英文:

> "Handle or declare. That's the law." - Head First

But, is it a good law? Let me give an example 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() throws exception and m1() calls m2(), meaning it must either handle or declare it. Hmmm let's declare it. Then main() calls m1() and it has same poll: declare or handle. I again decided to declare it and code compiles just fine.

Okay, it works, but who handled this exception at all? Looks like no one did. I know I am a beginner, but I don't like the sound of that. Yes, some methods can decide whether to declare or handle exceptions, but why main()? Shouldn't main method be one that just handles? In that way, no exception could "slip".

Am I missing something to this? I was honestly surprised that it is okay for main method to just declare exception, knowing that it is the last place we could technically catch something.

答案1

得分: 6

> 谁处理了这个异常?

Java运行时处理了。

更具体地说,根据Java语言规范(JLS)第11.3节“异常的运行时处理”中的规定,UncaughtExceptionHandler 处理了异常:

> 如果找不到可以处理异常的 catch 子句,那么当前线程(遇到异常的线程)将被终止。在终止之前,将执行所有的 finally 子句,并根据以下规则处理未捕获的异常:
>
> - 如果当前线程已经设置了未捕获异常处理程序,则会执行该处理程序。
>
> - 否则,将为当前线程的父线程组调用方法 uncaughtException。如果 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() 方法中简单地返回一样,除非代码已经启动了仍在运行的非守护线程,程序将结束。

英文:

> who handled this exception at all?

The Java runtime did.

More specifically, the UncaughtExceptionHandler did, as specified in the Java Language Specification (JLS), section 11.3. Run-Time Handling of an Exception:

> If no catch clause that can handle an exception can be found, then the current thread (the thread that encountered the exception) is terminated. Before termination, all finally clauses are executed and the uncaught exception is handled according to the following rules:
>
> - If the current thread has an uncaught exception handler set, then that handler is executed.
>
> - Otherwise, the method uncaughtException is invoked for the ThreadGroup that is the parent of the current thread. If the ThreadGroup and its parent ThreadGroups do not override uncaughtException, then the default handler's uncaughtException method is invoked.

So, by default, when main() throws an exception, unchecked or checked, the built-in default "uncaught exception handler" will simply print the stacktrace to System.err, as-if the following was executed right before main() was called:

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

After the "uncaught exception handler" has been invoked the thread is terminated, same as if you simply return from the main() method, and unless the code has started non-daemon threads that are still running, the program ends.

答案2

得分: 0

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

英文:

Application may contain more than one class with main method. In that case application should declare a manifest to know which main is entrypoint (first called method). Main method can be called from another methods or another main method as a static method and can throw any exception. If you don't catch exception at least in entrypoint, then exception returned from your application to java virtual machine, then mashine deside what todo with exception. Usually jvm prints error message and return value other than 0 to operating system.

huangapple
  • 本文由 发表于 2020年10月20日 02:14:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/64433029.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定