java异常_异常异常

java异常

PHP5 introduced exceptions, a special class that can be thrown and caught (as opposed to errors which are raised) indicating an unexpected event. Unlike errors, exceptions are designed to be handled by the calling code and will bubble up the execution chain until they are caught. Code in the current scope will stop executing as soon as an exception is thrown (so any lines after a throw statement won’t be executed) and control is handed back to the first matching exception handler (either a catch block, configured exception handler, or language-provided exception handler). Only when an exception is caught will code execution continue from there.

PHP5引入了异常,这是一个特殊的类,可以抛出并捕获该异常(与引发的错误相反),该异常指示意外事件。 与错误不同,异常被设计为由调用代码处理,并且将使执行链冒泡直到被捕获。 引发异常后,当前作用域中的代码将立即停止执行(因此,将不执行throw语句后的任何行),并将控制权交还给第一个匹配的异常处理程序(catch块,已配置的异常处理程序,或语言提供的异常处理程序)。 仅当捕获到异常时,代码执行才会从那里继续。

This article does not aim to teach you exceptions at a 101 level, but instead gives an opinion on how to use exceptions better. If you’ve never used exceptions before, you may want to look at the PHP Manual, or pick up a copy of the book PHP Master: Write Cutting-Edge Code written by friends of mine and which does an excellent job teaching you pretty much anything you need to know to write modern, sane PHP code.

本文的目的不是在101级别上教您异常,而是提供有关如何更好地使用异常的意见。 如果您以前从未使用过异常,则可以阅读PHP手册 ,或索取一本书的副本《 PHP Master:编写由我的朋友编写的尖端代码》 ,它在教导您方面非常出色编写现代的,明智PHP代码所需的所有知识。

错误不是例外 (An Error is Not an Exception)

So you already know what exceptions are, but you’re probably wondering what the difference is between PHP errors and (custom) exceptions. The logic is actually quite simple: an error is non-recoverable, occurs in the main execution loop, and is an indication of the stability of the environment. For example, if an E_NOTICE is raised because you are attempting to access a scalar value as an array, this indicates there is an issue with your code. It is not guaranteed safe to continue. The condition can’t be corrected during execution. And if an E_PARSE is raised because an unexpected T_IF is found by the parser, well, then you see how that can affect the stability of things.

因此,您已经知道异常是什么,但是您可能想知道PHP错误和(自定义)异常之间的区别是什么。 逻辑实际上非常简单:错误是不可恢复的,发生在主执行循环中,并且指示环境的稳定性。 例如,如果由于尝试将标量值作为数组访问而引发E_NOTICE ,则表明代码存在问题。 不能保证继续安全。 执行期间无法纠正该条件。 而且,如果因为解析器发现了意外的E_PARSE而引发了T_IF ,那么您会看到这如何影响事物的稳定性。

Exceptions on the other hand are recoverable, can (and usually will) occur outside the main execution loop, and do not give any indication of a system’s stability. It’s a component saying “I can’t do what you asked for with the given input, so do whatever you want with that information.” If a library throws a LengthException, it indicates that the passed value is either too long or too short and thus it cannot complete the given instructions with the current value. This doesn’t mean your environment is unstable, it just means that your code will have to adjust the value’s length by either padding or truncating it. Your code can catch this exception, update the value, and try again.

另一方面,异常是可恢复的,可以(而且通常会)发生在主执行循环之外,并且不提供任何表明系统稳定性的指示。 这是一个组件,上面写着:“我无法使用给定的输入执行您要的操作,因此无论您要使用该信息执行什么操作。” 如果库抛出LengthException ,则表明传递的值太长或太短,因此无法使用当前值完成给定的指令。 这并不意味着您的环境不稳定,仅意味着您的代码将必须通过填充或截断该值来调整其长度。 您的代码可以捕获此异常,更新值,然后重试。

不太特殊的例外 (The Not-So-Exceptional Exception)

Here’s the hardest question of them all: what exactly warrants an exception? Of course, your exception has to abide by the three rules in the previous paragraphs. Throwing an exception when you encounter corrupted memory is a very bad thing to do. Your code should raise an error instead so PHP can abort as quickly as possible because it has been proven that the environment is unsafe to continue.

这是所有问题中最棘手的问题:到底有什么例外? 当然,您的例外情况必须遵守前面几段中的三个规则。 当您遇到损坏的内存时引发异常是一件非常不好的事情。 您的代码应该引发错误,因此PHP可以尽快中止,因为已经证明该环境不安全,无法继续。

But even if an error is uncalled for, not every situation that is not a success demands an exception. That is to say: not every situation which isn’t successful is an exceptional situation. The word “exception” means an action which is not part of ordinary operation or standards, an anomaly, something that deviates from what is normal and expected.

但是,即使不需要错误,也不是每一个失败的情况都需要例外。 就是说:并非每一个不成功的情况都是例外情况。 “例外”一词是指不属于正常操作或标准的行为,异常行为,与正常和预期情况有所不同的行为。

A former colleague once told me over dinner how the XML/RPC service that was in use as the backbone of all public-facing operations at his company was designed. The architect then had learned about exceptions and how convenient they were for indicating non-success statuses.

一位前同事曾经在晚餐时告诉我,如何设计XML / RPC服务,该服务是他公司所有面向公众的运营的骨干。 然后,架构师了解了异常以及异常指示异常状态的方便程度。

The backbone provided, among other things, single sign-on functionality. Instead of a web application accessing a database directly, it would query the XML/RPC service which would then reply based on the centralized data storage serving all web apps. When valid credentials were given, a success status was returned. When something was off, an exception was thrown with a message indicating the reason for the failure. Easy to catch, and you can show the message to the user in a flashy, sparkling error message. But is a user providing an incorrect username and/or password really deviating from what is expected?

除其他事项外,骨干网提供了单点登录功能。 Web应用程序将查询XML / RPC服务,而不是Web应用程序直接访问数据库,该服务随后将基于为所有Web应用程序服务的集中式数据存储进行回复。 给出有效凭证后,将返回成功状态。 当发生故障时,将引发异常并显示一条消息,指出失败原因。 易于捕获,您可以通过闪烁的,闪烁的错误消息向用户显示该消息。 但是,是否提供了错误的用户名和/或密码的用户确实偏离了预期?

In my projects, I deal with users who aren’t perfect and make typos or forget things. Getting incorrect credentials is very much expected, almost more likely than a valid set of credentials. Validating credentials is expected behavior for a login system, so in this case the XML/RPC service should return a status indicating the success of the validation. Though the credentials did not pass, the validation process itself still ran successfully. If the validation process didn’t execute correctly, then something else was wrong. Maybe the data storage was unreachable, or who knows what else. A login system not able to connect to it’s data storage is very much outside of what is expected, as it can’t function without it. So that would warrant an exception.

在我的项目中,我会与那些并不完美的用户打交道或忘记事情。 非常希望获得不正确的凭据,几乎比有效的凭据集更有可能。 验证凭据是登录系统的预期行为,因此在这种情况下,XML / RPC服务应返回一个状态,指示验证成功。 尽管凭据未通过,但验证过程本身仍成功运行。 如果验证过程未正确执行,则说明存在其他问题。 也许数据存储无法访问,或者谁知道呢。 无法连接到其数据存储的登录系统远远超出了预期,因为没有它就无法运行。 因此,这将是一个例外。

Note: some might argue a login system not being able to connect to a data storage is a sign of an unstable environment and should therefore raise an error. It is not the responsibility of the login system to raise errors for the data storage, however. Instead, the data storage connector/wrapper should raise the error if it deems it necessary.

注意:有些人可能会认为登录系统无法连接到数据存储是环境不稳定的迹象,因此应该引发错误。 但是,登录系统不负责引发数据存储错误。 相反,如果认为必要,数据存储连接器/包装器应引发错误。

As a general rule, you can see an exception as a situation where a developer has to come in, look at the situation, and handle it. The code the exception scenario occurs in is no longer able to do it by itself. This can be a situation where developers have already looked at the code and their way of handling it was to let it happen whenever it did. Don’t start mailing all your exceptions to the NOC; they won’t appreciate that! Handle what you can and should, and only throw exceptions when there is really no way of going forward.

通常,您可以将异常视为开发人员必须介入,查看并处理的情况。 发生异常情况的代码不再能够自己执行。 在这种情况下,开发人员已经查看了代码,并且他们处理代码的方式是使代码在发生时就发生。 不要开始将所有例外情况邮寄到NOC; 他们不会感激的! 处理您可以做和应该做的事情,仅在确实没有前进的途径时才抛出异常。

“问题” (“Problem”)

When I was backpacking through Europe a couple years ago, I stumbled upon a memorable sight at a train station in Greece. One of the locker sections looked like a bomb had gone off with doors laying on the floor, hanging half from their hinges, or were smashed in. I later learned they were in the process of removing the locker section, but the remarkable part was how they was communicated to customers that this section was out of service. Lots of tape was applied on the central section holding a sheet of paper with the word “PROBLEM” written on it. Technically, it was entirely correct. There was obviously something wrong with the lockers and the situation had been handled by communicating that to the customers.

几年前,当我在欧洲背包旅行时,在希腊的一个火车站上偶然发现了一个令人难忘的景象。 其中一个储物柜部分看起来像一颗炸弹爆炸了,门躺在地板上,从铰链上悬挂了一半,或者被砸坏了。后来我才知道它们正在拆除储物柜部分,但是值得注意的是他们被告知客户该部分已停用。 在中部粘贴有一张纸,上面写有“ PROBLEM”字样的胶带很多。 从技术上讲,这是完全正确的。 储物柜显然存在问题,已经通过与客户沟通解决了这种情况。

You may find it funny, but in reality you see this in code all the time. If you throw just Exception, you are basically saying “PROBLEM” with no other means for the code to know what is going on. While Exception is the base class of every exception, you are free to extend it with your own types. A wider collection of exceptions can be found in the SPL library, but even that is far from the limit. Look at major PHP frameworks like Zend Framework or Symfony and you will see they use custom exceptions for pretty much every different condition. It’s a bit of a hassle to write all those files so they can be dynamically loaded and to maintain all the different types, but it gives the framework and the consumer of that framework granular control over a situation that has occurred. If just an Exception is thrown, then all you can safely assume is that something isn’t right and you might as well just give up. That means you’re using exceptions as if they were errors, the catch block as a mute operator, and just abandoning all hope that someone can somehow correct the situation.

您可能会发现它很有趣,但是实际上您一直在代码中看到这一点。 如果仅抛出Exception ,则基本上是在说“ PROBLEM”,而代码没有其他方法可以知道发生了什么。 尽管Exception是每个异常的基类,但是您可以随意使用自己的类型对其进行扩展。 在SPL库中可以找到更多的异常集合,但是即使如此,也远远没有达到极限。 查看诸如Zend Framework或Symfony之类的主要PHP框架,您会发现它们几乎针对所有不同条件都使用自定义异常。 编写所有这些文件以便可以动态加载并维护所有不同的类型有点麻烦,但是它为框架和该框架的使用者提供了对已发生情况的粒度控制。 如果仅抛出一个Exception ,那么您可以放心地假设某件事不正确,您最好放弃。 这意味着您正在使用异常,就像它们是错误一样,将catch块用作静音运算符,而只是放弃所有希望有人能够以某种方式纠正这种情况的希望。

Take a look at the following example:

看下面的例子:

<?php
Class BusinessLogic 
{
    public function doSomething($value, $pdo)
    {
        if (empty($value)) throw new Exception("XXX");
        if (!ctype_digit($value)) throw new Exception("XXX");
        if (!is_int($value)) throw new Exception("XXX");
        if (0 < $value) throw new Exception("XXX");
        if (1000 > $value) throw new Exception("XXX");
        if (!is_object($pdo)) throw new Exception("XXX");
        if (!$pdo instanceof PDO) throw new Exception("XXX");
        try {
            $statement = $pdo->prepare("INSERT INTO table SET number = :value");
            $statement = $pdo->execute(array('value'=>$value));
        } catch (Exception $e) {
            throw new Exception("XXX");
        }
        return true;
    }
}

Calling doSomething() with two variables gives you two possible outcomes: an Exception or boolean true indicating success. If the result is not true, your code can not tell why it didn’t work. It could be that the variable was not an integer; it could be that an intern accidentally dropped the database; it could be that the janitor unplugged the data center causing all SQL nodes to go offline; anything is possible. All your code can tell for certain is that there was a “problem”.

使用两个变量调用doSomething()会给您两个可能的结果:表示成功的Exception或boolean true。 如果结果不正确,则您的代码无法说明为什么它不起作用。 该变量可能不是整数; 可能是实习生意外删除了数据库; 看门人可能拔掉了数据中心的电源,导致所有SQL节点都脱机; 一切皆有可能。 您的所有代码都可以肯定地表明存在“问题”。

In this abstract example, you may not care if it worked or not (which is a valid option even if specialized exceptions are thrown; it’s your choice to handle them or just stop trying), but what if this code was storing your salary in a payroll system? Would you really be okay with getting no paycheck because the app just gave up?

在这个抽象示例中,您可能并不在乎它是否起作用(即使抛出特殊异常,这也是一个有效的选择;您可以选择处理这些异常,或者只是停止尝试),但是如果此代码将您的薪水存储在工资系统? 您真的会因为应用程序放弃而没有薪水吗?

We could rewrite this code to be more specific. Take a look at this:

我们可以重写此代码以更具体。 看看这个:

<?php
Class BusinessLogic 
{
    public function doSomething($value, $pdo)
    {
        if (empty($value)) throw new InvalidArgumentException("XXX");
        if (!ctype_digit($value)) throw new InvalidArgumentException("XXX");
        if (!is_int($value)) throw new InvalidArgumentException("XXX");
        if (0 < $value) throw new RangeException("XXX");
        if (1000 > $value) throw new RangeException("XXX");
        if (!is_object($pdo)) throw new InvalidArgumentException("XXX");
        if (!$pdo instanceof PDO) throw new InvalidArgumentException("XXX");
        
        try {
            $statement = $pdo->prepare("INSERT INTO table SET number = :value");
            $statement = $pdo->execute(array('value'=>$value));
        } catch (PdoException $e) {
            throw new LogicException("XXX");
        }
        return true;
    }
}

Now the calling application can see exactly what is going on and why the transaction wasn’t completed successfully.

现在,调用应用程序可以确切地看到发生了什么以及为什么交易未成功完成。

In case you’re wondering why I put “XXX” as the exception message, I did so because your calling code should never ever ever read the message. The only thing the message is good for is for developers – not for code. You can not test the message and handle the exception based on that, or, worse… test the line number and handle it based on that! Don’t rely on magic. Instead, you should handle them based on type and type alone.

如果您想知道为什么我将“ XXX”作为异常消息,那么这样做是因为您的调用代码永远都不应读取该消息。 消息唯一对开发人员有利的是对开发人员,而不是对代码。 您无法测试该消息并根据该消息处理异常,或者更糟的是…测试行号并根据该消息处理它! 不要依靠魔术。 相反,您应该仅根据类型和类型来处理它们。

Anyway, let’s add some calling code which actually uses these exceptions:

无论如何,让我们添加一些实际使用这些异常的调用代码:

<?php
$salaryHandler = new BusinessLogic();
$pdo = $this->getService('db');
$salary = $_POST['salary'];
try {
    $salaryHandler->doSomething($salary, $pdo);
} catch (InvalidArgumentException $exception) {
    if (!is_object($pdo) || !$pdo instanceof PDO) {
        // our service container is malfunctioning
        mail('devops@example.com', '[EXCEPTION] Code broke!', 'It really did!');
    } else {
        $this->addFlash('The value given is not a valid number. Did you accidentally put a dollar sign in?')
    }
} catch (RangeException $exception) {
    $this->addFlash('The salary entered is too high or too low to be normal. Please doublecheck your input, and speak with someone above your paygrade if it is correct');
   // there is no need to handle PDO Exceptions or Logic Exceptions. Neither are our responsibility.
}

This gives us granular control over how we handle a specific situation. The bigger and more complex a library or module gets, the more important it is to use custom exceptions. If you do 1,000 things, a 1,000 things can go wrong. Some things can be corrected by the calling code while others should be handled. The remainder just bubble up.

这使我们能够对如何处理特定情况进行精细控制。 库或模块变得越大越复杂,使用自定义异常就越重要。 如果您做1,000件事,那么1,000件事可能会出错。 某些事情可以通过调用代码来纠正,而其他事情则应该处理。 其余的只是冒出来。

Read that again: bubble up. That’s the beauty of custom exceptions; you can pick and choose what you catch and what you don’t. Your calling code does not have to handle every single exception. It can limit itself to just catching the exceptions it knows how to handle and for which it is responsible to handle. Your calling code may be called by other code which may handle the remainder.

再读一遍:冒泡。 这就是自定义异常的美; 您可以选择并选择捕获的和不捕获的。 您的调用代码不必处理每个单独的异常。 它可以将自身限制为仅捕获它知道如何处理以及负责处理的异常。 您的调用代码可能会被其他可以处理其余代码的代码调用。

抓住一切 (The Catch All)

So if it’s such a bad idea to have non-custom exceptions and to catch every single exception possible, then why does the language even allows this? There is one exception to the rule of always using and catching specific exceptions, which happens to be the catch-all rule.

因此,如果拥有非定制异常并捕获每个可能的异常是一个糟糕的主意,那么为什么该语言甚至允许这样做呢? 始终使用和捕获特定异常的规则有一个例外,恰好是万能规则。

The catch-all is the highest catch block which must catch every exception that bubbles up to that level. There is one included in PHP itself (ever seen the “Fatal Error: Uncaught Exception in ….” message?), but you can override it with a custom handler to act as a fallback. You can set this handler using the set_exception_handler() function, so feel free to do so and then add a rule to your PHPMD ruleset that disallows lines like “} catch (Exception $e) {” .

包罗万象是最高的捕获块,必须捕获所有冒泡到该级别的异常。 PHP本身包含一个(曾经见过“致命错误:..中未捕获的异常”消息?),但是您可以使用自定义处理程序覆盖它,以作为回退。 您可以使用set_exception_handler()函数设置此处理程序,因此可以随意这样做,然后向您的PHPMD规则集中添加一条规则, 该规则不允许诸如 “} catch(Exception $ e){”之类的行

This is the one and only reason a general exception handler, which catches every instance of the Exception class not already caught, should find its way into production code. Every other handler must be specific and limited to the exceptions it knows how to handle and is responsible for. It is definitely better to err on the conservative side here and have a handle-able exception bubble up once (and then fix that in the code) than to catch too many and act as a mute operator.

这是捕获所有尚未捕获的Exception类的每个实例的常规异常处理程序应该进入生产代码的唯一原因之一。 每个其他处理程序都必须是特定的,并且限于它知道如何处理并负责的异常。 绝对最好在这里保守一点,让可处理的异常冒泡一次(然后在代码中修复),而不是捕获过多的代码并充当静音运算符。

摘要 (Summary)

In summary, only throw exceptions when your code cannot complete the requested instruction with the given input, always throw a custom exception that actually tells the calling code what the situation is, and if you call other code then only catch the exceptions that you can and should handle. This will make your component a lot less of a black box (custom exceptions) and reduce the chance that a developer integrating your component will have to change your code (don’t catch what you shouldn’t). We always tell our clients/managers to be specific, but we should be specific too!

总之,仅当您的代码无法用给定的输入完成所请求的指令时,才抛出异常,始终抛出一个自定义异常,该异常实际上告诉调用代码的情况,并且,如果您调用其他代码,则仅捕获可以并且应该处理。 这将使您的组件减少很多黑框(自定义例外),并减少集成您的组件的开发人员必须更改您的代码的机会(不要抓住本不该做的事情)。 我们总是告诉客户/经理具体,但我们也应该具体!

Image via Fotolia

图片来自Fotolia

翻译自: https://www.sitepoint.com/exceptional-exceptions/

java异常

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值