Lecture
Это окончание невероятной информации про try-catch.
...
font-size: 14px;">(Not to be confused with Structured Exception Handling.)
Structured exception handling requires mandatory support on the part of the programming language — the presence of special syntactic constructs. Such a construct contains a block of controlled code and an exception handler (or handlers). The most general form of such a construct (conditional) is:
BlockStart ... // Controlled code ... if (condition) then CreateException Exception2 ... Handler Exception1 ... // Handler code for Exception1 Handler Exception2 ... // Handler code for Exception2 UnhandledHandler ... // Code for handling previously unhandled exceptions BlockEnd
Here «BlockStart» and «BlockEnd» are keywords that delimit the block of controlled code, while «Handler» marks the start of the block handling the corresponding exception. If an exception occurs inside the block, between the start and the first handler, control jumps to the handler written for it, after which the entire block terminates and execution continues with the command following it. In some languages there are no special keywords for delimiting the block of controlled code; instead, the exception handler(s) can be built into some or all of the syntactic constructs that combine several statements. So, for example, in the Ada language any compound statement (begin — end) can contain an exception handler.
«UnhandledHandler» is a handler for exceptions that don't match any of those described above in the given block. Exception handlers can in reality be described in different ways (one handler for all exceptions, one handler per exception type), but in principle they all work the same way: when an exception occurs, the first matching handler in the given block is found, its code executes, and then execution of the block terminates. Exceptions can arise both as a result of program errors and by being explicitly generated using the corresponding command (in the example, the «CreateException» command). From the handlers' point of view, such artificially created exceptions are no different from any others.
Exception-handling blocks can be nested inside one another multiple times, both explicitly (textually) and implicitly (for example, a block calls a procedure that itself has an exception-handling block). If none of the handlers in the current block can handle the exception, execution of that block terminates immediately, and control is transferred to the nearest suitable handler at a higher level of the hierarchy. This continues until a handler is found that handles the exception, or until the exception exits all the handlers defined by the programmer and is passed to the default system handler, which terminates the program abnormally.
Sometimes it's inconvenient to finish handling an exception in the current block, i.e. it's desirable that when an exception occurs in the current block, the handler perform some actions, but that the exception continue to be handled at a higher level (this usually happens when the current block's handler doesn't fully handle the exception, but only partially). In such cases, a new exception is generated in the exception handler, or the previously raised one is resumed using a special command. The handler code is not protected within this block, so an exception created in it will be handled in blocks at a higher level.
In addition to blocks of controlled code for exception handling, programming languages can support blocks with guaranteed completion. Using them turns out to be convenient when, in some block of code, regardless of whether any errors occurred, certain actions need to be performed before it finishes. The simplest example: if some local object is dynamically created in memory within a procedure, then before exiting that procedure the object must be destroyed (to avoid a memory leak), regardless of whether errors occurred after its creation or not. This capability is implemented with blocks of code of the form:
BlockStart
... // Main code
Finalization
... // Cleanup code
BlockEnd
The statements enclosed between the «BlockStart» and «Finalization» keywords (the main code) execute sequentially. If no exceptions occur while they execute, the statements between the «Finalization» and «BlockEnd» keywords (the cleanup code) then execute. But if an exception (any exception) occurs while the main code is executing, the cleanup code executes immediately, after which the entire block terminates, and the exception that arose continues to exist and propagate until it is caught by some higher-level exception-handling block.
The fundamental difference between a block with guaranteed completion and a handling block is that it doesn't handle the exception, but merely guarantees the execution of a certain set of operations before the handling mechanism kicks in. It's easy to see that a block with guaranteed completion can readily be implemented using the ordinary structured-handling mechanism (it's enough to place the exception-generation command right before the end of the controlled block and write the handler code correctly), but having a separate construct makes the code more transparent and protects against accidental errors.
People are bound to make mistakes, and programmers are people. Applications can crash or stop working for various reasons. A failure can occur during application development OR in production, when the application has already been released. Now this hiccup can be classified in three ways:
1) Bugs — When the cause of an error is a mistake made by the developer, it's called a bug. A developer can be very experienced but can still write bad code by mistake. For example, a declared file object might not be released and could later cause a memory leak, which is a bug. Usually, during the development of enterprise applications, testers catch bugs and classify them by severity. But there can be moments when even the testing team may miss the chance to catch a bug. Well, that's dangerous!
2) Exceptions — An exception can be a System exception or an Application exception. Now suppose a file being parsed by the code was deleted by someone from its search location; a "File Not Found" exception could then occur. Such exceptions are normally handled by well-written code using exception handlers. These errors usually occur at runtime. Sometimes they can be difficult to prevent, but they can certainly be handled by good code. There can be a situation where a programmer can only catch an exception with good code but cannot prevent it.
3) End-user errors — An error can be caused by input made by the end user. For example, an invalid string might be entered into a Textbox that expects a number. These types of errors can be handled using controls such as RegularExpressionValidator, or code that handles keyboard, mouse, or stylus input. If not effectively handled by the developer, these errors can cause terrible nightmares. For example, an application could be hacked or corrupted via SQL injection if the input fields allow bad input that can compromise the code.
The three points above represent the broad categories into which errors can be divided

There are many reasons for an exception, usually falling into the following categories:
To understand how Java exception handling works, you need to master the following three types of exceptions:
Checked exceptions: the most characteristic checked exceptions are those caused by errors or problems from the user that cannot be foreseen by the programmers. For example, opening a non-existent file raises an exception that simply cannot be ignored at compile time.Java exceptions can be divided into unchecked exceptions and checked exceptions.
The differences and relationships between these exceptions will be described in detail below:
Error: An Error class object is created by the Java
1. If there's a return statement inside try {}, the code in finally {} will run right after the try. When does it run, before or after the return?
Answer: it will run before the method returns to the caller.
2. How does the Java language handle exceptions? How do you use the keywords: throws, throw, try, catch, and finally?
Answer: Java uses object-oriented methods to handle exceptions, classifies different exceptions, and provides a good interface. In Java every exception is an object that is an instance of the Throwable class or one of its subclasses. When an exception occurs in a method, an exception object is created. The object contains information about the exception. The method that called it can catch the exception and handle it. Java exception handling is achieved using five keywords: try, catch, throw, throws, and finally. Generally, try is used to run the program's code. If the system generates an exception object, it can be caught based on its type or handled, always executing a block of code (finally); try is used to mark a section of the program for guarding against all exceptions; the catch clause right after a try block is used to specify the type of exception you want to catch; the throw statement is used to explicitly generate an exception; throws is used to declare that a method may raise various kinds of exceptions (naturally, multiple exceptions may be declared, separated by commas); finally is used to guarantee that a piece of code executes regardless of what abnormal conditions occur; try statements can be nested, and whenever a try statement is encountered, the abnormal structure is pushed onto the exception stack until all try statements have executed. If the next-level try statement doesn't handle the exception, the exception stack keeps popping until it encounters a try statement that handles this exception, or, finally, throws the exception to the JVM.
3. What are the similarities and differences between runtime exceptions and checked exceptions?
Answer: Abnormalities indicate abnormal conditions that can arise while a program is running. Runtime exceptions indicate abnormalities that can arise during the normal operation of the virtual machine. This is a normal operational error and typically doesn't occur if the program is developed without issues. A checked exception is tied to the context in which the program runs. Even if the program is designed correctly, this can still be caused by problems in usage. The Java compiler requires methods to declare the checked exceptions they may generate, but doesn't require them to declare uncaught runtime exceptions. Features such as inheritance are often misused with exceptions in object-oriented programming. The following recommendations are given for using exceptions in Effective Java:
For some reason, many programmers think that exceptions and errors are the same thing. Some constantly throw exceptions, some turn errors into exceptions via errorHandler. Some try to boost performance by using exceptions. But in reality, exceptions and errors are completely different mechanisms. You shouldn't replace one mechanism with the other. They were created for different purposes.
In its standard library (SPL), PHP provides a ready-made set of base classes and interfaces for exceptions. In version 7 this set was extended with the Throwable interface. Here's a diagram of all the types available in version 7 (image — link):
Exception has many merits; I'll describe just a few (perhaps I'll phrase things imprecisely or even sloppily, but I was simply too lazy to hunt for scientific terms to describe the advantages, so they're described «in my own words»):
PHP's capabilities for handling standard errors are extremely limited:
An exception is an exceptional but expected circumstance, something rare, but which can happen for a whole range of reasons. Obvious examples include cases where a file (say, a log file) cannot be found, or where user input doesn't convert to an integer. Thrown exceptions have to be caught. Errors are generally unrecoverable. Say, for example, you have a block of code that will insert a row into a database. Perhaps that call fails (duplicate identifier) — you'll want to have an "error", which in this case is an "Exception". When you insert those rows, you might do something like this
An error is a bug in the code that produces an incorrect result, which may or may not trigger an exception.
Some examples of errors:
You do some calculations, and due to rounding errors (say) it outputs "23.9" instead of "24". That would be an error, but it doesn't trigger an exception.
You construct a filename but specify the path incorrectly, which leads to a "file not found" exception. This may be a bug, but it will trigger an exception.
It's clear that the standard error-handling mechanism is obsolete and is present in the language only for compatibility reasons.
Errors are the old way of handling a runtime error condition. As a rule, the code makes a call to something like set_error_handler before executing some code, following the interrupt tradition of assembly language. Here's what BASIC code looks like.
on error :divide_error
print 1/0
print "this won't print"
:divide_error
if errcode = X
print "divide by zero error"
It was hard to make sure that set_error_handler would be called with the correct value. And worse, the call could be made from a separate procedure that would change the error handler. Plus, many times calls were interspersed with set_error_handler calls and handlers. It was easy for the code to quickly spiral out of control. Exception handling came to the rescue by formalizing the syntax and semantics of what good code actually did.
try {
print 1/0;
print "this won't print";
} catch (DivideByZeroException $e) {
print "divide by zero error";
}
There's no separate function or risk of calling the wrong error handler. Now the code is guaranteed to be in one place. Plus we get better error messages.
PHP only used error handling back when many other languages had already switched to the preferred exception-handling model. PHP developers eventually implemented exception handling. But, probably for the sake of supporting old code, they kept error handling around and provided a way to make error handling look like exception handling. Moreover, there's no guarantee that some piece of code can't reset the error handler that exception handling itself was supposed to provide.
The final answer
Errors that were coded before exception handling was implemented are probably still errors. New errors are probably exceptions. But there's no construct or logic dictating which are errors and which are exceptions. It was simply based on what was available at the time it was coded, and the preference of the programmer coding it.
Exceptions and errors are when the code does something wrong. A user more or less expects to enter incorrect login information at some point. Check whether the username/password is correct; if not, redirect the user to the login page ( header('location:login.php?failed=1'); ), and then, if $_GET['failed'] is set, display a message. That would be the simplest approach.
As for exceptions/errors … you should stick with exceptions. You throw an exception, and then you catch it and decide what to do. I think trigger_error is more intended for propagating an error back to the client when you don't know how to handle the error in a catch block.
Errors are something that cannot be fixed; all you can do is report them: write to the log, send an email to the developer, and apologize to the user. For example, if my engine can't connect to the DB, that's an error. Period. Full stop. Without the DB the site doesn't work, and there's nothing I can do about it. So I call ales_kaput() and trigger_error(), and my errorHandler sends me an email and shows the visitor the message «Sorry, the site isn't working».
Exceptions aren't errors; they're merely special situations that need to be handled somehow. For example, if you try to divide by zero in a calculator, the calculator won't hang, won't send a message to the developer, and won't apologize to you. Such situations can be handled with an ordinary if. Strictly speaking, exceptions are a language construct that lets you control the flow of execution. It's a construct on the same level as if, for, and return. That's it. This mechanism is nothing more than that. Just flow control.
Their main purpose: propagating through a cascade. Let me show this with an example: there are three functions that call each other in a cascade:
a();
function a()
{
b();
}
function b()
{
c(99);
}
function c($x)
{
if ($x === 0) {
// Some special situation,
// that should stop the execution of functions c() and b(),
// and function a() should find out about it
}
return 100 / $x;
}
This task could be solved without the exception mechanism. For example, you could make all the functions return a special type (if you're a seasoned PHP veteran, you should remember PEAR_Error). For simplicity I'll just use null:
a();
function a()
{
echo 'a-begin';
$result = b();
if ($result === null) {
echo 'Делить на ноль нехорошо';
return;
}
echo 'a-stop';
}
function b()
{
echo 'b-begin';
$result = c(0);
if ($result === null) {
return null;
}
echo 'b-stop';
return true;
}
function c($x)
{
echo 'c-begin';
if ($x === 0) {
return null;
}
echo 'c-stop';
return 100 / $x;
}
The result:
a-begin
b-begin
c-begin
Делить на ноль нехорошо
The task is accomplished, but notice that I had to modify the intermediate function b() so that it propagates the result of the function below it up the cascade. But what if I have a cascade of 5 or 10 functions? Then I'd have to modify ALL the intermediate functions. And what if the exceptional situation happens in a constructor? Then I'd have to resort to workarounds.
Now here's the solution using Exception:
a();
function a()
{
echo 'a-begin';
try {
b();
echo 'a-stop';
} catch (Exception $e) {
echo $e->getMessage();
}
}
function b()
{
echo 'b-begin';
c(0);
echo 'b-stop';
}
function c($x)
{
echo 'c-begin';
if ($x === 0) {
throw new Exception('Делить на ноль нехорошо');
}
echo 'c-stop';
return 100 / $x;
}
The execution result will be identical. Function b() remains in its original form, untouched. This is especially relevant if you have long cascades. And the $e object can also contain additional information about what happened.
So it turns out that errors and exceptions are completely different tools for solving completely different problems:
an error — an unfixable situation;
an exception – lets you interrupt the execution of a cascade of functions and pass along some information. Something like a global return statement. If you don't have a cascade, then it's enough to just use if or return.
Some might object: «Look at Zend Framework — they always throw exceptions there. That's best practice, and you should do the same. Even if the DB connection fails, you should throw an exception».
I want to dispel this misconception. Zend really is best practice, but the Zend developers are in a different boat and doing different things. The fundamental difference between them and me is that they're writing a universal library that will be used in many projects. And from their vantage point, they can't say what is a critical error and what is a recoverable one.
For example, your project might have several MySQL servers and you might switch between them if one of them goes down. That's why Zend_Db, as a universal library, throws an exception, and what to do with it is up to you to decide. Exception is flexible — you decide for yourself at which level and what type of situations to catch. You can display an error message or try to fix the situation that arose, if you know how. When writing universal libraries you should always throw exceptions. This makes the library more flexible.
In the end, I can say that both mechanisms have their own characteristics and, most importantly, their own purposes, and these purposes don't overlap at all. Errors != exceptions. Don't use exceptions to improve performance or for error messages. Don't implement any situation-fixing logic in the My_Custom_Exception class. That class should be empty; it's created only to define the type of situation and to catch just what's needed. The class name 'My_Custom_Exception' is a kind of tree-like analog of the flat list of E_*** constants (E_NOTICE, E_WARNING, ...).
PHP developed its error-handling mechanism long ago, and it works great. I use it happily wherever it's needed.
Now is the time to ask: «Why do we need such a complicated thing if we can just use an if statement?».
The first answer: «Because that way the code is more readable, and error handling is moved into a separate block». If we used an if statement, the code would look like this:
You'll agree that in the earlier version the code looks clearer. But that's not all. Now the developer doesn't have to handle these errors directly — it's enough to throw an exception, and handling it can be dealt with at a higher level. Exceptions can also be passed up the chain:
Trying to get to the truth, I ran several experiments with different types of functions.
The first type returned a true status and was checked with if...else statements
The second type returned a false status and was checked with if...else statements
The third type simply performed actions and returned nothing. It was checked with a try...catch block
The fourth type always threw an exception and was checked with try...catch
Results:
Most modern programming languages, such as Ada, C++, D, Delphi, Objective-C, Java, JavaScript, Eiffel, OCaml, Ruby, Python, Common Lisp, SML, PHP, all the .NET platform languages, and others, have built-in support for structured exception handling. In these languages, when an exception supported by the language occurs, the call stack unwinds up to the first exception handler of a matching type, and control is transferred to the handler.
Aside from minor syntactic differences, there are really only a couple of approaches to exception handling. In the most common one, an exceptional situation is generated by a special operator (throw or raise), and the exception itself, from the program's point of view, is some data object. That is, generating an exception consists of two stages: creating the exception object and raising the exceptional situation with that object as a parameter. Constructing such an object by itself does not cause an exception to be thrown. In some languages the exception object can be an object of any data type (including a string, a number, a pointer, and so on); in others — only of a predefined exception type (most often named Exception) and, possibly, its derived types (descendant types, if the language supports object-oriented features).
The scope of the handlers begins with the special try keyword, or simply the language's block-start marker (for example, begin), and ends before the description of the handlers (catch, except, resque). There can be several handlers, one after another, and each can specify the type of exception it handles. As a rule, no search is made for the most suitable handler, and the very first handler compatible in type with the exception is executed. Therefore the order in which the handlers appear matters: if a handler compatible with many or all exception types appears in the text before the specific handlers for particular types, the specific handlers will not be used at all.
Some languages also allow a special block (else) that executes if no exception was generated in the corresponding scope. More common is the ability to guarantee the completion of a block of code (finally, ensure). A notable exception is C++, where there is no such construct. Instead, the automatic invocation of object destructors is used. At the same time, there are non-standard C++ extensions that also support finally functionality (for example, in MFC).
Overall, exception handling might look as follows (in some abstract language):
try {
line = console.readLine();
if (line.length() == 0)
throw new EmptyLineException("Строка, считанная из консоли, пустая!");
console.printLine("Привет, %s!" % line);
}
catch (EmptyLineException exception) {
console.printLine("Привет!");
}
catch (Exception exception) {
console.printLine("Ошибка: " + exception.message());
}
else {
console.printLine("Программа выполнилась без исключительных ситуаций");
}
finally {
console.printLine("Программа завершается");
}
In some languages there may be only a single handler, which sorts out the different exception types itself.
The benefits of using exceptions are especially noticeable when developing libraries of procedures and software components intended for mass use. In such cases the developer often doesn't know exactly how a particular exceptional situation should be handled (when writing a universal file-reading routine it's impossible to anticipate in advance how to react to an error, since that reaction depends on the program using the routine), but the developer doesn't need to know — it's enough to generate an exception whose handler is left for the user of the component or routine to implement. The only alternative to exceptions in such cases is returning error codes, which then have to be passed along a chain through several layers of the program until they reach the point where they're handled, cluttering the code and reducing its clarity. Using exceptions for error control improves code readability, since it lets you separate error handling from the algorithm itself, and makes it easier to program and use other developers' components. And error handling can be centralized in aspects.
Unfortunately, the implementation of the exception-handling mechanism depends heavily on the language, and even compilers for the same language on the same platform can differ significantly. This prevents exceptions from being transparently passed between parts of a program written in different languages; for example, libraries that support exceptions are usually unsuitable for use in programs written in languages other than the ones they were developed for, and even more so in languages that don't support an exception-handling mechanism at all. This situation significantly limits the possibilities for using exceptions, for example in the UNIX OS and its clones, and under Windows, since most system software and low-level libraries on these systems are written in C, which doesn't support exceptions. Accordingly, to work with the APIs of such systems using exceptions, you have to write wrapper libraries whose functions would analyze the return codes of the API functions and generate exceptions when needed.
Supporting exceptions complicates the language and the compiler. It also slows down the program, since the cost of handling an exception is usually higher than the cost of handling an error code. Therefore, in parts of a program that are critical for speed, it's not recommended to raise and handle exceptions, although it should be noted that in application programming, cases where the difference in speed between exception handling and error codes is actually significant are very rare.
Correctly implementing exceptions can be difficult in languages with automatic destructor invocation. When an exception occurs in a block, the destructors of objects created in that block must be automatically invoked — but only those that haven't already been destroyed in the normal course of events. Furthermore, the requirement to interrupt the current operation when an exception occurs conflicts with the requirement for mandatory automatic destruction in languages with auto-destructors: if an exception occurs in a destructor, either the compiler is forced to destroy an object that isn't fully released, or the object remains in existence, i.e. a memory leak occurs. As a result, generating uncaught exceptions in destructors is simply forbidden in a number of cases.
Joel Spolsky believes that code designed to work with exceptions loses linearity and predictability. Whereas in classic code the exit points from a block, procedure, or function are only where the programmer explicitly placed them, in code with exceptions an exception can (potentially) occur at any statement, and it's impossible to tell from the code alone exactly where exceptions might occur. In code designed for exceptions, it's impossible to predict at what point an exit from a block of code will occur, and every statement must be treated as potentially the last one in the block; as a result, the code's complexity increases and its reliability decreases.
Also, in complex programs, large «piles» of try ... finally and try ... catch (try ... except) statements arise if aspects are not used.
The opposite view on the safety of exception handling was given by Tony Hoare in 1980, describing the Ada programming language as having «... many features and notations, many of which are unnecessary, and some of which, such as exception handling, are even dangerous. [...] Do not allow this language in its present state to be used in applications where reliability is critical [...]. The next rocket to go astray as a result of a programming language error may not be an exploratory space rocket on a harmless trip to Venus: it may be a nuclear warhead exploding over one of our own cities»
Exception handling is often mishandled in software, especially when there are multiple sources of exceptions; a data-flow analysis of 5 million lines of Java code found more than 1,300 exception-handling defects. [17] Citing numerous prior studies by others (1999–2004) as well as their own results, Weimer and Necula wrote that a serious problem with exceptions is that they «create hidden control-flow paths that are difficult for programmers to reason about».
Go was initially released without explicit exception handling, with its developers arguing that it obscures control flow. [18] Later, a panic/ recover exception mechanism was added to the language, which the Go authors advise using only for unrecoverable errors that should stop the entire process
Exceptions, like unstructured flow, increase the risk of resource leaks (for example, exiting a section locked by a mutex, or a section temporarily holding an open file) or of an inconsistent state. There are various methods for managing resources in the presence of exceptions, most often combining a disposal pattern with some form of unwind protection (for example, a finallyclause) that automatically releases the resource when control leaves the section of code.
Initially (for example, in C++) there was no formal discipline for describing, generating, and handling exceptions: any exception could be raised anywhere in the program, and if no handler was found for it in the call stack, program execution was aborted. If a function (especially a library function) generates exceptions, then for robust operation the program using it must catch all of them. When, for some reason, one of the possible exceptions ends up unhandled, an unexpected abnormal termination of the program occurs.
Such effects can be fought with organizational measures: by describing the possible exceptions arising in library modules in the corresponding documentation. But there always remains a chance of missing a needed handler due to an accidental error or a mismatch between the documentation and the code (which is far from rare). To completely rule out the loss of exception handling, a branch for handling «all other» exceptions has to be specially added to the handlers (which is guaranteed to catch any exception, even ones unknown in advance), but this solution isn't always optimal. Moreover, hiding all possible exceptions can lead to a situation where serious, hard-to-detect bugs are concealed.
Later, a number of languages, for example Java, introduced checked exceptions. The essence of this mechanism consists of adding the following rules and restrictions to the language:
Externally (in the Java language), an implementation of this approach looks as follows:
int getVarValue(String varName) throws SQLException {
... // the method's code, possibly containing calls that can throw an SQLException
}
// Compile error - the exception is neither declared nor caught
int eval1(String expression) {
...
int a = prev + getVarValue("abc");
...
}
// Correct — the exception is declared and will be propagated further
int eval2(String expression) throws SQLException {
...
int a = prev + getVarValue("abc");
...
}
// Correct — the exception is caught inside the method and doesn't propagate outward
int eval3(String expression) {
...
try {
int a = prev + getVarValue("abc");
} catch (SQLException ex) {
// Exception handling
}
...
}
Here the getVarValue method is declared as generating an SQLException. Consequently, any method that uses it must either catch this exception or declare it as one it generates. In this example, the eval1 method will cause a compile error, since it calls the getVarValue method but doesn't catch the exception or declare it. The eval2 method declares the exception, while the eval3 method catches and handles it; both of these methods are correct with respect to working with the exception raised by the getVarValue method.
Checked exceptions reduce the number of situations where an exception that could have been handled instead caused a critical error in the program, since the compiler keeps track of whether handlers are present. This is especially useful when code changes, when a method that previously couldn't throw an exception of type X starts doing so; the compiler will automatically track down all its usages and check for the presence of corresponding handlers.
Another useful quality of checked exceptions is that they encourage writing handlers thoughtfully: the programmer clearly sees the complete and correct list of exceptions that can arise at a given point in the program, and can write a meaningful handler for each one, instead of creating a «just in case» generic handler for all exceptions that reacts the same way to every abnormal situation.
Checked exceptions also have their drawbacks.
Because of the drawbacks listed above, when checked exceptions are mandatory, this mechanism is often worked around. For example, many libraries declare all their methods as throwing some common exception class (for example, Exception), and handlers are created only for that type of exception. The result is that the compiler forces you to write exception handlers even where they're objectively unnecessary, and it becomes impossible to determine, without reading the source, exactly which subclasses of the declared exceptions a method throws, in order to attach different handlers to them. A more correct approach is considered to be catching, inside the method, new exceptions produced by the called code, and, if necessary, passing the exception onward — «wrapping» it in an exception the method already returns. For example, if a method was changed so that it starts accessing a database instead of the file system, it can catch SQLException itself and throw a newly created IOException instead, specifying the original exception as the cause. It's usually recommended to initially declare exactly the exceptions that the calling code will have to handle. Say, if a method retrieves input data, it makes sense to declare IOException for it; and if it works with SQL queries, then, regardless of the nature of the error, it should declare SQLException. In any case, the set of exceptions thrown by a method should be carefully thought through. When necessary, it makes sense to create your own exception classes, inheriting them from Exception or other suitable checked exceptions.
It's impossible to make absolutely all exceptions checked, since some exceptional situations are by their nature such that they can occur at any, or almost any, point in the program, and the programmer is unable to prevent them. At the same time, it's pointless to list such exceptions in a function's description, since this would have to be done for every function, without making the program any clearer. These are mainly exceptions belonging to one of two kinds:
Taking such errors entirely outside the exception-handling system is illogical and inconvenient, if only because sometimes they are nevertheless caught and handled. That's why, in systems with checked exceptions, some exception types are excluded from the checking mechanism and work in the traditional way. In Java these are exception classes inherited from java.lang.Error — fatal errors — and java.lang.RuntimeException — runtime errors, usually associated with coding mistakes or insufficient checks in the program code (an invalid argument, dereferencing a null reference, an out-of-bounds array access, an invalid monitor state, and so on).
Часть 1 Try-catch exception handling: what's the difference between exceptions and errors?
Часть 2 How to create custom exceptions - Try-catch exception handling: what's
Часть 3 The Finally block - Try-catch exception handling: what's the difference
Часть 4 The difference between bugs, exceptions, and end-user errors - Try-catch
Comments