哪个更好/更有效:检查错误值或捕获Java中的exception

哪个在Java中效率更高:检查错误值以防止exception或让exception发生并捕获它们?

以下是两个示例代码块,用于说明这种差异:

void doSomething(type value1) { ResultType result = genericError; if (value1 == badvalue || value1 == badvalue2 || ...) { result = specificError; } else { DoSomeActionThatFailsIfValue1IsBad(value1); // ... result = success; } callback(result); } 

 void doSomething(type value1) { ResultType result = genericError; try { DoSomeActionThatFailsIfValue1IsBad(value1); // ... result = success; } catch (ExceptionType e) { result = specificError; } finally { callback(result); } } 

一方面,你总是在进行比较。 另一方面,老实说,我不知道系统的内部做什么来生成exception,抛出它,然后触发catch子句。 它有效率较低的声音,但如果它不会在非错误情况下增加开销,那么它的平均效率会更高。 这是什么? 它是否添加类似的检查? 是否在为exception处理添加的隐式代码中检查,即使有额外的显式检查层? 也许它总是取决于exception的类型? 我不考虑什么?

让我们假设所有“坏值”都是已知的 – 这是一个明显的问题。 如果您不知道所有错误值 – 或者列表太长且不规则 – 那么exception处理可能是唯一的方法。

那么,每种方法的优缺点是什么?为什么?

需要考虑的问题:

  • 如果在大多数情况下值为“坏”(会抛出exception),您的答案如何变化?
  • 这有多少取决于正在使用的VM的细节?
  • 如果要求语言-X提出同样的问题,答案会有所不同吗? (更一般地说,问题是否可以假设检查值总是比依赖exception处理更有效,因为它增加了当前编译器/解释器的开销。)
  • (新)抛出exception的行为很慢。 输入try块是否有开销,即使没有抛出exception?

与SO的相似之处:

  • 这类似于本答案中的代码示例,但声明它们仅在概念上相似,而不是编译现实。
  • 前提类似于这个问题,但在我的情况下,任务的请求者(例如“Something”)不是方法的调用者(例如“doSomething”)(因此没有返回)。
  • 这一个非常相似,但我没有找到我的问题的答案。

  • 类似于要列出的太多其他问题,除了:

    我不是在问理论最佳实践。 我要求更多关于运行时性能和效率(这应该意味着,对于特定情况,有非意见答案),特别是在资源有限的平台上。 例如,如果唯一的坏值只是一个空对象,那么检查它或者只是尝试使用它并捕获exception会更好/更有效吗?

大多数时候,如果价值”不好“(会抛出exception),你的答案如何改变? ”我认为那是关键。 与比较相比,例外是昂贵的,因此您确实希望在例外条件下使用例外。

同样,关于这个答案如何根据语言/环境而变化的问题与此相关:exception的费用在不同的环境中是不同的。 例如,在第一次抛出exception时,.Net 1.1和2.0 非常慢。

纯粹从效率的角度来看,并且考虑到您的代码示例,我认为这取决于您期望看到错误值的频率。 如果坏的值并不常见,那么进行比较会更快,因为exception是昂贵的。 但是,如果不良值很少,则使用exception可能会更快。

但最重要的是,如果您正在寻找性能,请分析您的代码。 这段代码甚至可能不是一个问题。 如果是,那么尝试两种方式,看看哪个更快。 同样,这取决于您期望看到不良价值的频率。

关于抛出例外的成本,我几乎找不到令人惊讶的当前信息。 很明显,必须有一些,你正在创建一个对象,并可能获得堆栈跟踪信息。

在具体的例子中,你谈到:

 if (value1 == badvalue || value1 == badvalue2 || ...) { result = specificError; } else { DoSomeActionThatFailsIfValue1IsBad(value1); // ... result = success; } 

这里的问题在于,如果(可能不完全)复制调用者中应该由您调用的方法拥有的逻辑,则存在危险。

因此我不会执行这些检查。 您的代码没有执行实验,它确实“知道”它应该发送的数据我猜想? 因此,抛出exception的可能性应该很低。 因此,保持简单,让被调用者进行检查。

在我看来,如果只有一个安全的运行系统,你应该尝试/捕获任何可能引发exception的块。 如果您检查可能的数据错误,则可以更好地控制错误响应。 所以我建议做两件事。

嗯,例外是更昂贵的,是的,但对我来说,它的重点是效率与糟糕设计的成本。 除非您的用例需要它,否则始终坚持最佳设计。

问题是,你什么时候抛出exception? 在特殊情况下。

如果您的参数不在您要查找的范围内,我建议您返回错误代码或布尔值。

例如,一种方法,

 public int IsAuthenticated(String username, String password) { if(!Validated(username,password) { // just an error // log it return -2; } // contacting the Database here if cannot connect to db { // woww this is HUUGE throw new DBException('cannot connect'); // or something like that } // validate against db here if validated, return 0; // etc etc } 

那是我2美分

我个人认为exception表明某些东西被破坏了 – 这可能是一个被非法参数调用的API,或者被零除或未找到文件等。这意味着可以通过检查值来抛出exception。

对于你的代码的读者 – 再次是我的个人观点 – 如果你可以确定它不被各种奇怪的抛出(如果用作其中一部分的伪装,那么它本身就是伪装的程序流程)。 你只需要少考虑一下。

这在我看来是件好事。 “智能”代码很难包装好。

从侧面说明 – JVM变得更加智能 – 编码效率通常不会带来回报。

通常,人们会认为try-catch更昂贵,因为它在代码中看起来更重,但这完全取决于JIT。 我的猜测是,如果没有真实案例和一些性能测量,就无法分辨。 比较可能更昂贵,尤其是当您有许多值时,或者因为您必须调用equals()因为==在许多情况下不起作用。

至于你应该选择哪一个(如“代码风格”),我的答案是:确保用户在失败时收到有用的错误消息。 其他任何事情都是品味问题,我不能给你规则。

为安全起见,假设exception是昂贵的。 它们通常是,如果它们不是,它至少会促使你明智地使用例外。 (进入try块通常很便宜,因为实现者尽力做到这一点,即使以使exception更加昂贵为代价。毕竟,如果正确使用exception,代码将经常多次进入try块它会抛出。)

更重要的是,例外是一种风格问题。 exception条件的例外使代码更简单,因为错误检查代码更少,因此实际function更清晰,更紧凑。

但是,如果在更正常的情况下可能会抛出exception,那么读者必须牢记无形的控制流,与Intercal的COME FROM...UNLESS...声明相当。 (Intercal是最早的笑话语言之一。)这非常令人困惑,很容易导致误读和误解代码。

我的建议适用于我所知道的每种语言和环境:

这里不要担心效率。 除了效率之外,还有很多理由以一种certificate有效的方式使用exception。

自由使用try块。

在特殊情况下使用例外。 如果可能发生exception,请测试它并以另一种方式处理它。

像这样的问题就像问,

“使用所有抽象函数编写接口或基类是否更有效”

哪个更有效率是否重要? 对于特定情况,其中只有一个是正确的方法

请注意,如果您的代码没有抛出exception,那么它并不总是暗示输入在边界内。 依赖标准Java(API + JVM)抛出exception,例如NullPointerExceptionArrayIndexOutOfBoundsExceptions是一种非常不健康的validation输入的方法。 垃圾进入有时会产生垃圾 – 但不会例外

是的,例外是非常昂贵的。 在正常的处理流程中不应抛出它们。

在优化方面,我认为你会发现它可能是一种清洗。 他们都会表现得很好,我不认为例外投掷会成为你的瓶颈。 你应该更关心Java的设计目标(以及其他Java程序员所期望的)以及抛出的exception。 Java是围绕抛出/捕获exception而设计的,你可以打赌设计师使这个过程尽可能高效。

我认为这主要是哲学和语言文化。 在Java中,一般公认的做法是方法签名是方法和调用它的代码之间的契约。 因此,如果您收到不正确的值,通常会抛出一个未经检查的exception,并让它在更高级别处理:

 public void setAge(int age) { if(age < 0) { throw new IllegalArgumentException("Array can't be negative"); } this.age = age; } 

在这种情况下,调用者打破了他们的合同结束,所以你将他们的输入吐回到他们的exception。 当您由于某种原因无法履行合同时,“投掷”条款将被使用。

 public void readFile(String filename) throws IOException { File myfile = new File(filename); FileInputStream fis = new FileInputStream(myfile); //do stuff fis.read(); //do more stuff } 

在这种情况下,作为方法编写者,您已经打破了合同的结束,因为用户提供了有效的输入,但由于IOException,您无法完成其请求。

希望有点让你走上正轨。 祝你好运!