java中捕获异常是什么原因_关于java:为什么捕获检查异常允许不抛出异常的代码?...

在Java中,抛出检查异常的方法(异常或它的子类型-IOExtRebug、中断ExtExcCEP等)必须声明抛出语句:

public abstract int read() throws IOException;

不声明throws语句的方法不能抛出已检查的异常。

public int read() { // does not compile

throw new IOException();

}

// Error: unreported exception java.io.IOException; must be caught or declared to be thrown

但是在Java中捕获安全方法中的检查异常仍然是合法的:

public void safeMethod() { System.out.println("I'm safe"); }

public void test() { // method guarantees not to throw checked exceptions

try {

safeMethod();

} catch (Exception e) { // catching checked exception java.lang.Exception

throw e; // so I can throw... a checked Exception?

}

}

实际上,不是。这有点滑稽:编译器知道e不是一个检查过的异常,并允许重新执行它。事情甚至有点荒谬,这段代码不会编译:

public void test() { // guarantees not to throw checked exceptions

try {

safeMethod();

} catch (Exception e) {

throw (Exception) e; // seriously?

}

}

// Error: unreported exception java.lang.Exception; must be caught or declared to be thrown

第一个片段是一个问题的动机。

编译器知道选中的异常不能在安全方法中抛出-所以它可能应该只允许捕获未选中的异常?

回到主要问题——有什么理由用这种方式实现捕获检查过的异常?这是设计上的一个缺陷,还是我遗漏了一些重要因素——可能是向后不兼容?如果在这种情况下只允许捕获RuntimeException会发生什么潜在的错误?我们非常欣赏这些例子。

关于主要问题:它本身不是设计中的一个缺陷,RuntimeExceptions是异常的子类,因此捕获异常也包括未检查的异常。也就是说,没有理由这样做,它甚至可能会使阅读代码的人感到困惑,因为他们可能认为safemethod()可能会抛出异常。我认为在这里捕获runtimeexception是更好的选择。

关于throw语句的jls的相关部分。

你甚至可以抓住Throwable。抓一个更普通的类型有什么问题?

@如果我们可以使用原始类型,那么为什么要使用泛型呢?答案是更强的编译器检查。编译器可以告诉我们不能抛出异常,但只能抛出RuntimeException,那么为什么要捕获它呢?

@Adamskywalker我们知道原始类型导致的许多问题。更广泛的类型会导致什么问题?这就是你的比喻失败的原因。根据您的论点,final Object ob ="foo";也会导致编译器错误,因为我们知道在编译时ob的运行时类型将是String。

@我同意这一点。

因为safeMethod()是安全的,这就意味着被抓到的Exception e必须是RuntimeException。如果它保持原样(如第一个片段中所示),那么一切都很好。但是,当您在第二个代码片段中显式地强制转换到Exception时,您会使编译器忘记它知道的内容,并相信它可能是任何Exception,当然这是不正常的。

引用Java语言规范,{112.3:

It is a compile-time error if a catch clause can catch checked exception class E1 and it is not the case that the try block corresponding to the catch clause can throw a checked exception class that is a subclass or superclass of E1, unless E1 is Exception or a superclass of Exception.

我猜想这个规则早在Java 7之前就已经出现了,在那里没有多捕获。因此,如果您有一个可以抛出许多异常的try块,那么捕获所有内容的最简单方法就是捕获一个公共超类(在最坏的情况下,如果您还想捕获Error,则为Exception或Throwable)。

请注意,您可能无法捕获与实际抛出的内容完全无关的异常类型-在您的示例中,捕获不是RuntimeException的Throwable的任何子类将是一个错误:

try {

System.out.println("hello");

} catch (IOException e) {  // compilation error

e.printStackTrace();

}

按op编辑:答案的主要部分是问题示例仅适用于异常类。通常,不允许在代码的随机位置捕获选中的异常。抱歉,如果我用这些例子把别人搞糊涂了。

确切地说,这个规则早在尝试多重捕获之前就存在了,可能是从1.0开始的(但肯定是从1.2开始的)。

抓到Exception并不能抓住一切。抓Throwable什么都抓。

您给出了一个包含多个异常的示例。如果抛出了多个异常并且至少检查了其中一个异常,我可以捕获公共父异常。但对于所有未检查的异常,都有一个公共的父级-runtimeexception。所以我仍然可以用一个catch子句处理多个未检查的异常

@Adamskywalker规则很简单:如果可以抛出异常E,则可以捕获E的任何超类。为什么这个规则在历史上是必要的,在答案中已经明确说明了。你所建议的问题是,这些规则对于解决一个根本不存在的问题会变得更加混乱和复杂。

Adamskywalker:是的,但是语言设计者/编译器作者也需要避免事情变得比必要的更复杂。正如@biziclop所建议的,您所建议的修改将解决一个非常小的问题,但会使规则及其实现变得复杂。

@廷宾德:我想把重点放在例外上,但我同意我最初的措辞让它听起来像例外是唯一可以抛出的东西。我已经编辑了我的答案。

@Adamskywalker:而且,由于任何东西都可以抛出一个Error,所以必须始终允许捕获Throwable,因此规则看起来更加复杂:"您可以捕获所有可能抛出的异常类型的任何子类,并且可以捕获Throwable的最低公共祖先。"-vs"您可以捕获可能抛出的任何子类或超类。"

Java 7引入了更具包容性的异常类型检查。

However, in Java SE 7, you can specify the exception types FirstException and SecondException in the throws clause in the rethrowException method declaration. The Java SE 7 compiler can determine that the exception thrown by the statement throw e must have come from the try block, and the only exceptions thrown by the try block can be FirstException and SecondException.

本文讨论的是一个try块,专门抛出FirstException和SecondException;虽然catch块抛出Exception,但方法只需声明它抛出FirstException和SecondException,而不是Exception:

public void rethrowException(String exceptionName)

throws FirstException, SecondException {

try {

// ...

}

catch (Exception e) {

throw e;

}

}

这意味着编译器可以检测到在test中抛出的唯一可能的异常类型是Errors或RuntimeExceptions,两者都不需要捕获。当您使用throw e;时,即使静态类型是Exception时,它也可以告诉您不需要声明或重新捕获。

但是当你把它投射到Exception时,这就绕过了这个逻辑。现在,编译器将其视为需要捕获或声明的普通Exception。

将此逻辑添加到编译器中的主要原因是,当重新调用捕获这些特定子类型的通用Exception时,程序员只允许在throws子句中指定特定的子类型。但是,在这种情况下,它允许您捕获一个通用的Exception,而不必在throws子句中声明任何异常,因为没有可以抛出的特定类型是检查异常的。

我理解为什么第一个代码编译而第二个代码不编译。我想我应该用粗体字把主要问题括起来。

主要问题在标题中——如果我们知道不能抛出异常,为什么还要允许捕获它呢?

在语义层面上,我不相信这能完全解释正在发生的事情。是的,它描述了行为,但我不认为这类事情才是真正的答案。

@AdamskyWalker总是可以捕获所有可能抛出的异常的超类,例如,想象一个文件操作方法,它包装了可以抛出FileNotFoundException和IIOException的代码。在try-multi-catch之前,处理这两个问题的唯一方法是要么有两个相同的catch条款,要么抓住它的一个超类,例如IOException。或Exception。这两种解决方案在某种程度上都是不好的,但第二种方案则稍差一点。

可以抛出类型Exception。外面有很多坏代码,它们的方法只是声明throws Exception。可以构造和抛出异常本身,而无需进一步对其进行子类化。像Throwable一样,所有可以抛出的东西的超级类型。

@Biziclop我写了我对超类捕捉的想法,回答是

@还有一些相当合理的案例,比如反思。

这里的问题是,选中/未选中的异常限制会影响代码允许抛出的内容,而不是允许捕获的内容。虽然您仍然可以捕获任何类型的Exception,但只有未选中的类型才允许您再次实际抛出。(这就是将未选中的异常强制转换为选中的异常会破坏代码的原因。)

用Exception捕获未检查的异常是有效的,因为未检查的异常(a.k.a.RuntimeExceptions)是异常的一个子类,并且遵循标准的多态性规则;它不会将捕获的异常转换为Exception,就像在Object中存储String不会将String转换为EDOCX1一样。〔30〕。多态性意味着一个可以保存Object的变量可以保存来自Object的任何东西(例如String)。同样,由于Exception是所有异常类型的超类,因此,Exception类型的变量可以保存从Exception派生的任何类,而不必将对象转换为Exception类型。考虑一下:

import java.lang.*;

// ...

public String iReturnAString() { return"Consider this!"; }

// ...

Object o = iReturnAString();

尽管变量的类型是Object,但o仍然存储String,不是吗?同样,在您的代码中:

try {

safeMethod();

} catch (Exception e) { // catching checked exception

throw e; // so I can throw... a checked Exception?

}

实际上这意味着"捕获任何与类EDCOX1,4"兼容的任何东西(即EDCOX1,4和它派生的任何东西)。同样的逻辑也在其他语言中使用;例如,在C++中,捕获一个EDCOX1,45个也将捕获EDCOX1,46,EDCOX1,47,EDCOX1,48,任何适当定义的用户创建的异常,等等。n,因为它们都来自于std::exception。

tl;dr:你没有捕捉到选中的异常,你捕捉到了任何异常。只有将异常转换为选中的异常类型时,该异常才会成为选中的异常。

@阿达姆斯基沃克,请不要轻率地对待那些只想帮助你的人。多态性的观点是完全正确的,因为它的核心是catch (Exception ex)与Exception ex = ...非常相似。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值