📜 ⬆️ ⬇️

Exceptions in Java, Part I (try-catch-finally)

This is the first part of an article devoted to such a linguistic mechanism of Java as exceptions (the second (checked / unchecked) here ). It has an introductory character and is designed for novice developers or those who are just starting to learn a language.

I also teach Scala for Java Developers on the udemy.com online education platform (equivalent to Coursera / EdX).

1. Keywords: try, catch, finally, throw, throws
2. Why use System.err, not System.out
3. The compiler needs to return the result (or requires silence)
4. Nonlocal control transfer (nonlocal control transfer)
5. try + catch (catch - polymorphic)
6. try + catch + catch + ...
7. try + finally
8. try + catch + finally
9. Embedded try + catch + finally
')

1. Keywords: try, catch, finally, throw, throws


The exception mechanism in Java is supported by five keywords.


The “magic” (i.e., some behavior not reflected in the source code and therefore not repeatable by the user) of exceptions # 1 is that catch, throw, throws can be used exclusively with java.lang.Throwable or its descendants.

throws:
Fits
public class App { public static void main(String[] args) throws Throwable {} } 


No good
 public class App { public static void main(String[] args) throws String {} } >> COMPILATION ERROR: Incompatible types: required 'java.lang.Throwable', found: 'java.lang.String' 


catch:
Fits
 public class App { public static void main(String[] args) { try { } catch (Throwable t) {} } } 


No good
 public class App { public static void main(String[] args) { try { } catch (String s) {} } } >> COMPILATION ERROR: Incompatible types: required 'java.lang.Throwable', found: 'java.lang.String' 


throw:
Fits
 public class App { public static void main(String[] args) { // Error -  Throwable throw new Error(); } } 


No good
 public class App { public static void main(String[] args) { throw new String("Hello!"); } } >> COMPILATION ERROR: Incompatible types: required 'java.lang.Throwable', found: 'java.lang.String' 


In addition, a throw requires a non-null argument, otherwise a NullPointerException at the time of execution
 public class App { public static void main(String[] args) { throw null; } } >> RUNTIME ERROR: Exception in thread "main" java.lang.NullPointerException 


throw and new are two independent operations. In the following code, we independently create an exception object and “throw” it.
 public class App { public static void main(String[] args) { Error ref = new Error(); //   throw ref; // ""  } } >> RUNTIME ERROR: Exception in thread "main" java.lang.Error 


However, try to analyze this.
 public class App { public static void main(String[] args) { f(null); } public static void f(NullPointerException e) { try { throw e; } catch (NullPointerException npe) { f(npe); } } } >> RUNTIME ERROR: Exception in thread "main" java.lang.StackOverflowError 



2. Why use System.err, not System.out



System.out is the output buffered stream, but System.err is not. Thus, the output can be as
 public class App { public static void main(String[] args) { System.out.println("sout"); throw new Error(); } } >> RUNTIME ERROR: Exception in thread "main" java.lang.Error >> sout 


This is how it is (err outpaced out when outputting to the console)
 public class App { public static void main(String[] args) { System.out.println("sout"); throw new Error(); } } >> sout >> RUNTIME ERROR: Exception in thread "main" java.lang.Error 


Let's draw it
    +----------------+ +->| msg2 msg1 msg0 | --> out / +----------------+ \ / +-> +--------+   | | \ +-> +--------+ \ / +------------------------> err  ,   

when you write to System.err - your message is immediately output to the console, but when you write to System.out, it may be buffered for a while. Stacktrace unhandled exception is displayed via System.err, which allows them to overtake "normal" messages.


3. The compiler needs to return the result (or requires silence)



If the method declaration says that it returns NOT void, then the compiler keeps a sharp eye out that we would return an instance of the required type or an instance of the type, which can implicitly lead to the required
 public class App { public double sqr(double arg) { //  double return arg * arg; // double * double -  double } } 

 public class App { public double sqr(double arg) { //  double int k = 1; //  int return k; //    int  double } } 

 //   ,   -    public class App { public double sqr(double arg) { //  double int k = 1; //  int return (double) k; //   int  double } } 


here so will not pass (other type)
 public class App { public static double sqr(double arg) { return "hello!"; } } >> COMPILATION ERROR: Incompatible types. Required: double. Found: java.lang.String 


That will not work - no return
 public class App { public static double sqr(double arg) { } } >> COMPILATION ERROR: Missing return statement 


and so it will not work (the compiler cannot make sure that there will be a return)
 public class App { public static double sqr(double arg) { if (System.currentTimeMillis() % 2 == 0) { return arg * arg; //  currentTimeMillis() -  ,    } //   ,   ? } } >> COMPILATION ERROR: Missing return statement 


The compiler keeps track of the fact that we would return something, because otherwise it is not clear what this program should have printed
 public class App { public static void main(String[] args) { double d = sqr(10.0); // ,    d? System.out.println(d); } public static double sqr(double arg) { // nothing } } >> COMPILATION ERROR: Missing return statement 


Because of the funny, you can not return anything, but "hang the method"
 public class App { public static double sqr(double arg) { while (true); // ,  ! } } 


Here nothing will ever be assigned to d, since the sqr method hangs
 public class App { public static void main(String[] args) { double d = sqr(10.0); // sqr -  "",  System.out.println(d); // d -     ! } public static double sqr(double arg) { while (true); //      "" } } 


The compiler will skip the “plug” (still take a square OR hang)
 public class App { public static double sqr(double arg) { if (System.currentTimeMillis() % 2 == 0) { return arg * arg; //  ,   double } else { while (true); //   ""  } } } 


But the exception mechanism allows NOTHING TO RETURN ANYTHING!
 public class App { public static double sqr(double arg) { throw new RuntimeException(); } } 


So, we have THREE options for the compiler.
 public class App { public static double sqr(double arg) {//       double long time = System.currentTimeMillis(); if (time % 2 == 0) { return arg * arg; // ,   double } else if (time % 2 == 1) { { while (true); // ,   "" } else { throw new RuntimeException(); //    } } } 


But WHAT DOES double return a function that throws a RuntimeException?
And NO!
 public class App { public static void main(String[] args) { // sqr - "" (  "" ), double d = sqr(10.0); //   main()      // d -     ! System.out.println(d); //      ! } public static double sqr(double arg) { throw new RuntimeException(); // ""  } } >> RUNTIME ERROR: Exception in thread "main" java.lang.RuntimeException 


To summarize: an exception that is thrown is an optional return type. If your method has declared that it returns double, but you do not have a double, you can throw an exception. If your method has declared that it returns nothing (void), but you still have something to say, you can throw an exception.

Let's look at some example from practice.

Task: implement a function that calculates the area of ​​a rectangle
 public static int area(int width, int height) {...} 

It is important that the assignment sounds like this, in terms of the subject area - “calculate the area of ​​a rectangle,” and not in terms of the decision to “multiply two numbers”:
 public static int area(int width, int height) { return width * height; //    } 


Question: what to do if we find that at least one of the arguments is a negative number?
If you simply multiply, then we missed the erroneous data further. Worse, perhaps we “rectified the situation” - they said that the area of ​​the rectangle with two negative sides is -10 and -20 = 200.

We can not return nothing
 public static int area(int width, int height) { if (width < 0 || height < 0) { //    ,  } else { return width * height; } } >> COMPILATION ERROR: Missing return statement 


You can, of course, unsubscribe to the console, but who will read it and how to determine where there was a breakdown. With that, the calculation will continue with incorrect data
 public static int area(int width, int height) { if (width < 0 || height < 0) { System.out.println("Bad ..."); } return width * height; } 


You can return a special value, indicating that something is wrong (error code), but who guarantees that it will be read, and not just take advantage of it?
 public static int area(int width, int height) { if (width < 0 || height < 0) { return -1; //  ""   } return width * height; } 


We can, of course, completely stop the virtual machine
 public static int area(int width, int height) { if (width < 0 || height < 0) { System.exit(0); } return width * height; } 


But the “right way” is this: if you find possible incorrect behavior, then
1. Stop calculations, generate a crash message that is hard to ignore, provide the user with information about the cause, give the user the opportunity to repair everything (load the laundry back and press the start button again)
 public static int area(int width, int height) { if (width < 0 || height < 0) { throw new IllegalArgumentException("Negative sizes: w = " + width + ", h = " + height); } return width * height; } 



4. Nonlocal control transfer (nonlocal control transfer)


The mechanism of exceptional situations (exceptions) is the mechanism of NONLOCAL TRANSFER OF CONTROL.
What does this mean?
The program, in the course of its execution (more precisely, the execution of instructions within a separate thread), operates with a stack ("stack") of frames. The transfer of control is carried out either within a single frame.
 public class App { public static void main(String[] args) { // :   int x = 42; //   int y = x * x; //   x = x * y; //   ... } } 

 public class App { public static void main(String[] args) { // :   if (args.length > 2) {   //     ... } else { //   ... } //   ... } } 

 public class App { public static void main(String[] args) { // :   do..while int x = 1; do { ... } while (x++ < 10); ... } } 

and other operators.

Either the transfer of control occurs in a “stack” of frames between NEIGHBOR frames.


return - exit from ONE frame (from frame # 4 (h () method))
 public class App { public static void main(String[] args) { System.err.println("#1.in"); f(); //  ,   ,     System.err.println("#1.out"); //  } //    ,   public static void f() { System.err.println(". #2.in"); g(); //  ,   ,     System.err.println(". #2.out"); // } //    ,   public static void g() { System.err.println(". . #3.in"); h(); //  ,   ,     System.err.println(". . #3.out"); //  } //    ,   public static void h() { System.err.println(". . . #4.in"); if (true) { System.err.println(". . . #4.RETURN"); return; //      'return' } System.err.println(". . . #4.out"); //  } } >> #1.in >> . #2.in >> . . #3.in >> . . . #4.in >> . . . #4.RETURN >> . . #3.out >> . #2.out >> #1.out 


throw - exit all frames
 public class App { public static void main(String[] args) { System.err.println("#1.in"); f(); //  ,   ,     System.err.println("#1.out"); // ! } public static void f() { System.err.println(". #2.in"); g(); //  ,   ,     System.err.println(". #2.out"); // ! } public static void g() { System.err.println(". . #3.in"); h(); //  ,   ,     System.err.println(". . #3.out"); // ! } public static void h() { System.err.println(". . . #4.in"); if (true) { System.err.println(". . . #4.THROW"); throw new Error(); //      (" ")  'throw' } System.err.println(". . . #4.out"); // ! } } >> #1.in >> . #2.in >> . . #3.in >> . . . #4.in >> . . . #4.THROW >> RUNTIME ERROR: Exception in thread "main" java.lang.Error 


With the help of catch, we can stop a flying exception (the reason we automatically leave frames).
Stop after 3 frames, fly through frame # 4 (method h ()) + fly through frame # 3 (method g ()) + frame # 2 (method f ())
 public class App { public static void main(String[] args) { System.err.println("#1.in"); try { f(); //  ,   ,     } catch (Error e) { // "" ""  System.err.println("#1.CATCH"); //   } System.err.println("#1.out"); //   } public static void f() { System.err.println(". #2.in"); g(); //  ,   ,     System.err.println(". #2.out"); // ! } public static void g() { System.err.println(". . #3.in"); h(); //  ,   ,     System.err.println(". . #3.out"); // ! } public static void h() { System.err.println(". . . #4.in"); if (true) { System.err.println(". . . #4.THROW"); throw new Error(); //      (" ")  'throw' } System.err.println(". . . #4.out"); // ! } } >> #1.in >> . #2.in >> . . #3.in >> . . . #4.in >> . . . #4.THROW >> #1.CATCH >> #1.out 

Note that the standard script was restored in the main () method (frame # 1)

Stop after 2 frames, fly through frame # 4 (h () method) + fly through frame # 3 (g () method)
 public class App { public static void main(String[] args) { System.err.println("#1.in"); f(); //  ,   ,     System.err.println("#1.out"); //    } public static void f() { System.err.println(". #2.in"); try { g(); //  ,   ,     } catch (Error e) { // "" ""  System.err.println(". #2.CATCH"); //   } System.err.println(". #2.out"); //   } public static void g() { System.err.println(". . #3.in"); h(); //  ,   ,     System.err.println(". . #3.out"); // ! } public static void h() { System.err.println(". . . #4.in"); if (true) { System.err.println(". . . #4.THROW"); throw new Error(); //      (" ")  'throw' } System.err.println(". . . #4.out"); // ! } } >> #1.in >> . #2.in >> . . #3.in >> . . . #4.in >> . . . #4.THROW >> . #2.CATCH >> . #2.out >> #1.out 


Stop after 1 frame (actually analogous to return, just left the frame “in another way”)
 public class App { public static void main(String[] args) { System.err.println("#1.in"); f(); //  ,   ,     System.err.println("#1.out"); //    } public static void f() { System.err.println(". #2.in"); g(); //  ,   ,     System.err.println(". #2.out"); //    } public static void g() { System.err.println(". . #3.in"); try { h(); //  ,   ,     } catch (Error e) { // "" ""  System.err.println(". . #3.CATCH"); //   } System.err.println(". . #3.out"); //   } public static void h() { System.err.println(". . . #4.in"); if (true) { System.err.println(". . . #4.THROW"); throw new Error(); //      (" ")  'throw' } System.err.println(". . . #4.out"); // ! } } >> #1.in >> . #2.in >> . . #3.in >> . . . #4.in >> . . . #4.THROW >> . . #3.CATCH >> . . #3.out >> . #2.out >> #1.out 


So let's bring it all into one picture.
 // --- RETURN--- // --- THROW--- //   1-  //    ( 4)  #1.in #1.in . #2.in . #2.in . . #3.in . . #3.in . . . #4.in . . . #4.in . . . #4.RETURN . . . #4.THROW . . #3.out RUNTIME EXCEPTION: Exception in thread "main" java.lang.Error . #2.out #1.out // --- THROW+CATCH--- //   3-  //   2-  //   1-  #1.in #1.in #1.in . #2.in . #2.in . #2.in . . #3.in . . #3.in . . #3.in . . . #4.in . . . #4.in . . . #4.in . . . #4.THROW . . . #4.THROW . . . #4.THROW #1.CATCH . #2.CATCH . . #3.CATCH #1.out . #2.out . . #3.out #1.out . #2.out #1.out 



5. try + catch (catch - polymorphic)



Recall the exception hierarchy
  Object | Throwable / \ Error Exception | RuntimeException 


The fact that exceptions are objects is important to us in two ways.
1. They form a hierarchy with the root java.lang.Throwable (java.lang.Object is the ancestor of java.lang.Throwable, but Object is no longer an exception)
2. They may have fields and methods (we will not use this in this article)

On the first point: catch is a polymorphic construction, i.e. catch of type Parent intercepts flying instances of any type that is Parent (ie, instances of Parent directly or of any Parent descendant)
 public class App { public static void main(String[] args) { try { System.err.print(" 0"); if (true) {throw new RuntimeException();} System.err.print(" 1"); } catch (Exception e) { // catch  Exception  RuntimeException System.err.print(" 2"); } System.err.println(" 3"); } } >> 0 2 3 


Even so: in the catch block we will have an Exception type reference to an object of type RuntimeException
 public class App { public static void main(String[] args) { try { throw new RuntimeException(); } catch (Exception e) { if (e instanceof RuntimeException) { RuntimeException re = (RuntimeException) e; System.err.print(" RuntimeException   !!!"); } else { System.err.print("    RuntimeException???"); } } } } >>  RuntimeException   !!! 


catch by descendant cannot catch ancestor
 public class App { public static void main(String[] args) throws Exception { //   'throws' try { System.err.print(" 0"); if (true) {throw new Exception();} System.err.print(" 1"); } catch (RuntimeException e) { System.err.print(" 2"); } System.err.print(" 3"); } } >> 0 >> RUNTIME EXCEPTION: Exception in thread "main" java.lang.Exception 


catch for one "brother" cannot catch another "brother" (Error and Exception are not in relation to the descendant ancestor, they are from parallel branches of inheritance from Throwable)
 public class App { public static void main(String[] args) { try { System.err.print(" 0"); if (true) {throw new Error();} System.err.print(" 1"); } catch (Exception e) { System.err.print(" 2"); } System.err.print(" 3"); } } >> 0 >> RUNTIME EXCEPTION: Exception in thread "main" java.lang.Error 


In previous examples, I hope you noticed that if an exception is caught, then the JVM executes the statements following AFTER the last try + catch brackets.
But if not intercepted, then we
1. don't go to the catch block
2. leave the frame method with the flying exception

And what will happen if we went into catch, and then threw an exception out of catch?
 public class App { public static void main(String[] args) { try { System.err.print(" 0"); if (true) {throw new RuntimeException();} System.err.print(" 1"); } catch (RuntimeException e) { //  RuntimeException System.err.print(" 2"); if (true) {throw new Error();} //   Error } System.err.println(" 3"); //  -   Error } } >> 0 2 >> RUNTIME EXCEPTION: Exception in thread "main" java.lang.Error 

In this case, the execution of the method is also interrupted (do not print "3"). The new exception has nothing to do with try-catch.

We can even throw the object that we have "on hand"
 public class App { public static void main(String[] args) { try { System.err.print(" 0"); if (true) {throw new RuntimeException();} System.err.print(" 1"); } catch (RuntimeException e) { //  RuntimeException System.err.print(" 2"); if (true) {throw e;} //       } System.err.println(" 3"); //  -   RuntimeException } } >> 0 2 >> RUNTIME EXCEPTION: Exception in thread "main" java.lang.RuntimeException 


And we will not get into other catch sections, if they are
 public class App { public static void main(String[] args) { try { System.err.print(" 0"); if (true) {throw new RuntimeException();} System.err.print(" 1"); } catch (RuntimeException e) { //  RuntimeException System.err.print(" 2"); if (true) {throw new Error();} //    Error } catch (Error e) { //   cath  Error "",       System.err.print(" 3"); } System.err.println(" 4"); } } >> 0 2 >> RUNTIME EXCEPTION: Exception in thread "main" java.lang.Error 

Please note that we did not print "3", although we have Error flying and "below" is located catch by Error. But the important point is that catch is related exclusively to the try-section, but not to other catch-sections.

As we show below, you can build nested constructs, but here’s an example that “corrects” this situation.
 public class App { public static void main(String[] args) { try { System.err.print(" 0"); if (true) {throw new RuntimeException();} System.err.print(" 1"); } catch (RuntimeException e) { //  RuntimeException System.err.print(" 2.1"); try { System.err.print(" 2.2"); if (true) {throw new Error();} //    Error System.err.print(" 2.3"); } catch (Throwable t) { //  Error System.err.print(" 2.4"); } System.err.print(" 2.5"); } catch (Error e) { //   cath  Error "",       System.err.print(" 3"); } System.err.println(" 4"); } } >> 0 2.1 2.2 2.4 2.5 4 



6. try + catch + catch + ...


As you have seen, we can arrange several catch after one try.

But there is such a rule - you can not put a descendant after an ancestor! (RuntimeException after Exception)
 public class App { public static void main(String[] args) { try { } catch (Exception e) { } catch (RuntimeException e) { } } } >> COMPILATION ERROR: Exception 'java.lang.RuntimeException' has alredy been caught 


Put brother after brother - you can (RuntimeException after Error)
 public class App { public static void main(String[] args) { try { } catch (Error e) { } catch (RuntimeException e) { } } } 


How is the selection of the "correct" catch? It's very simple - the JVM goes from top to bottom until it finds such a catch that it indicates your exception or its ancestor - it goes there. Below - does not go.
 public class App { public static void main(String[] args) { try { throw new Exception(); } catch (RuntimeException e) { System.err.println("catch RuntimeException"); } catch (Exception e) { System.err.println("catch Exception"); } catch (Throwable e) { System.err.println("catch Throwable"); } System.err.println("next statement"); } } >> catch Exception >> next statement 


The choice of catch is carried out in runtime (and not in compile-time), it means that it is not the type of LINKS (Throwable) that is taken into account, but the type of REFERENCE (Exception)
 public class App { public static void main(String[] args) { try { Throwable t = new Exception(); //   Throwable     Exception throw t; } catch (RuntimeException e) { System.err.println("catch RuntimeException"); } catch (Exception e) { System.err.println("catch Exception"); } catch (Throwable e) { System.err.println("catch Throwable"); } System.err.println("next statement"); } } >> catch Exception >> next statement 



7. try + finally


finally section gets control if try block completes successfully
 public class App { public static void main(String[] args) { try { System.err.println("try"); } finally { System.err.println("finally"); } } } >> try >> finally 


The finally section gets control, even if the try block has ended with an exception
 public class App { public static void main(String[] args) { try { throw new RuntimeException(); } finally { System.err.println("finally"); } } } >> finally >> Exception in thread "main" java.lang.RuntimeException 


the finally section gets control, even if the try block has ended with an exit directive
 public class App { public static void main(String[] args) { try { return; } finally { System.err.println("finally"); } } } >> finally 


a finally section is NOT called only if we have nailed the JVM
 public class App { public static void main(String[] args) { try { System.exit(42); } finally { System.err.println("finally"); } } } >> Process finished with exit code 42 


System.exit (42) and Runtime.getRuntime (). Exit (42) are synonyms
 public class App { public static void main(String[] args) { try { Runtime.getRuntime().exit(42); } finally { System.err.println("finally"); } } } >> Process finished with exit code 42 


And with Runtime.getRuntime (). Halt (42) - also does not have time to go to finally
 public class App { public static void main(String[] args) { try { Runtime.getRuntime().halt(42); } finally { System.err.println("finally"); } } } >> Process finished with exit code 42 


exit () vs halt ()
javadoc: java.lang.Runtime # halt (int status)
... Unlike the exit method, finalizers for on-exit has been enabled. This is not the case for the shutdown sequence.

However, the finally section cannot “fix” the try block of the completed exception (note that “more” is not displayed in the console)
 public class App { public static void main(String[] args) { try { System.err.println("try"); if (true) {throw new RuntimeException();} } finally { System.err.println("finally"); } System.err.println("more"); } } >> try >> finally >> Exception in thread "main" java.lang.RuntimeException 


The trick with "if (true) {...}" is required, because otherwise the compiler detects an unreachable code (last line) and refuses to compile it
 public class App { public static void main(String[] args) { try { System.err.println("try"); throw new RuntimeException(); } finally { System.err.println("finally"); } System.err.println("more"); } } >> COMPILER ERROR: Unrechable statement 


And the finally section cannot “prevent” the exit from the method if the try block calls return (“more” is not output to the console)
 public class App { public static void main(String[] args) { try { System.err.println("try"); if (true) {return;} } finally { System.err.println("finally"); } System.err.println("more"); } } >> try >> finally 


However, the finally section can "kill" throw / return with another throw / return
 public class App { public static void main(String[] args) { System.err.println(f()); } public static int f() { try { return 0; } finally { return 1; } } } >> 1 


 public class App { public static void main(String[] args) { System.err.println(f()); } public static int f() { try { throw new RuntimeException(); } finally { return 1; } } } >> 1 


 public class App { public static void main(String[] args) { System.err.println(f()); } public static int f() { try { return 0; } finally { throw new RuntimeException(); } } } >> Exception in thread "main" java.lang.RuntimeException 


 public class App { public static void main(String[] args) { System.err.println(f()); } public static int f() { try { throw new Error(); } finally { throw new RuntimeException(); } } } >> Exception in thread "main" java.lang.RuntimeException 


The finally section can be used for a final action that is guaranteed to be called (even if an exception was thrown or the author used return) at the end of the work
 // open some resource try { // use resource } finally { // close resource } 


For example, to release captured lock
 Lock lock = new ReentrantLock(); ... lock.lock(); try { // some code } finally { lock.unlock(); } 


Or to close an open file stream.
 InputStream input = new FileInputStream("..."); try { // some code } finally { input.close(); } 


Especially for these purposes in Java 7 appeared try-with-resources , we will study it later.

Generally speaking, in the finally section it is not possible to know in a standard way whether there was an exception.
Of course, you can try to write your "bike"
 public class App { public static void main(String[] args) { System.err.println(f()); } public static int f() { long rnd = System.currenttimeMillis(); boolean finished = false; try { if (rnd % 3 == 0) { throw new Error(); } else if (rnd % 3 == 1) { throw new RuntimeException(); } else { // nothing } finished = true; } finally { if (finished) { //    } else { //  ,  ? } } } } 


Not recommended practices
- return from the finally section (we can wipe the exception from the try block)
- actions in the finally section that can throw an exception (we can wipe the exception from the try block)


8. try + catch + finally



No exception
 public class App { public static void main(String[] args) { try { System.err.print(" 0"); // nothing System.err.print(" 1"); } catch(Error e) { System.err.print(" 2"); } finally { System.err.print(" 3"); } System.err.print(" 4"); } } >> 0 1 3 4 

Do not go in catch, go in finally, continue after the statement

There is an exception and there is a suitable catch.
 public class App { public static void main(String[] args) { try { System.err.print(" 0"); if (true) {throw new Error();} System.err.print(" 1"); } catch(Error e) { System.err.print(" 2"); } finally { System.err.print(" 3"); } System.err.print(" 4"); } } >> 0 2 3 4 

We come in catch, we go in finally, we continue after the operator

There is an exception but there is no suitable catch
 public class App { public static void main(String[] args) { try { System.err.print(" 0"); if (true) {throw new RuntimeException();} System.err.print(" 1"); } catch(Error e) { System.err.print(" 2"); } finally { System.err.print(" 3"); } System.err.print(" 4"); } } >> 0 3 >> RUNTIME ERROR: Exception in thread "main" java.lang.RuntimeException 

We do not go in catch, we go in finally, we do not continue after the operator - we fly out with an uncaught exception


9. Embedded try + catch + finally


Operators usually allow unlimited nesting.
Example with if
 public class App { public static void main(String[] args) { if (args.length > 1) { if (args.length > 2) { if (args.length > 3) { ... } } } } } 


Example with for
 public class App { public static void main(String[] args) { for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; i++) { for (int k = 0; k < 10; k++) { ... } } } } } 


The bottom line is that try-cacth-finally also allows unlimited nesting.
For example like this
 public class App { public static void main(String[] args) { try { try { try { ... } catch (Exception e) { } finally {} } catch (Exception e) { } finally {} } catch (Exception e) { } finally {} } } 


Or even like this
 public class App { public static void main(String[] args) { try { try { ... } catch (Exception e) { ... } finally { ... } } catch (Exception e) { try { ... } catch (Exception e) { ... } finally { ... } } finally { try { ... } catch (Exception e) { ... } finally { ... } } } } 


Well, let's explore how it works.

Nested try-catch-finally without exception
 public class App { public static void main(String[] args) { try { System.err.print(" 0"); try { System.err.print(" 1"); //  System.err.print(" 2"); } catch (RuntimeException e) { System.err.print(" 3"); //   -   } finally { System.err.print(" 4"); //   } System.err.print(" 5"); //  -    } catch (Exception e) { System.err.print(" 6"); //   -   } finally { System.err.print(" 7"); //   } System.err.print(" 8"); //  -    } } >> 0 1 2 4 5 7 8 

We do NOT go into both catch-sections (there is no exception), we go into both finally-sections and execute both strings AFTER finally.

An enclosed try-catch-finally with an exception that INTERCEPTES an INSIDE catch.
 public class App { public static void main(String[] args) { try { System.err.print(" 0"); try { System.err.print(" 1"); if (true) {throw new RuntimeException();} System.err.print(" 2"); } catch (RuntimeException e) { System.err.print(" 3"); //  -   } finally { System.err.print(" 4"); //   } System.err.print(" 5"); //  -     } catch (Exception e) { System.err.print(" 6"); //   -  ,   } finally { System.err.print(" 7"); //   } System.err.print(" 8"); //  -     } } >> 0 1 3 4 5 7 8 

catch- ( «3»), catch- ( «6», catch), finally- ( «4» «7»), finally ( «5» «8», catch).

try-catch-finally , catch
 public class App { public static void main(String[] args) { try { System.err.print(" 0"); try { System.err.print(" 1"); if (true) {throw new Exception();} System.err.print(" 2"); } catch (RuntimeException e) { System.err.print(" 3"); //   -  ,    } finally { System.err.print(" 4"); //   } System.err.print(" 5"); //   -     } catch (Exception e) { System.err.print(" 6"); //  -    } finally { System.err.print(" 7"); //   } System.err.print(" 8"); //  -     } } >> 0 1 4 6 7 8 

catch- ( «3»), catch- ( «6»), finally- ( «4» «7»), finally ( «5», ), finally ( «8», ).

try-catch-finally ,
 public class App { public static void main(String[] args) { try { System.err.print(" 0"); try { System.err.print(" 1"); if (true) {throw new Error();} System.err.print(" 2"); } catch (RuntimeException e) { System.err.print(" 3"); //   -  ,    } finally { System.err.print(" 4"); //   } System.err.print(" 5"); //   -     } catch (Exception e) { System.err.print(" 6"); //   -  ,    } finally { System.err.print(" 7"); //   } System.err.print(" 8"); //   -     } } >> 0 1 4 7 >> RUNTIME EXCEPTION: Exception in thread "main" java.lang.Error 

catch- ( «3» «6»), finally- ( «4» «7») finally ( «5» «8», ), .

Contacts


I do Java online training (here are the programming courses ) and publish some of the training materials as part of the reworking of the Java Core course . You can see videos of lectures in the audience on the youtube channel , perhaps the video of the channel is better organized in this article .

My teaching method is that I
  1. showing different uses
  2. I build a complex sequence of examples for each option
  3. explain the logic of moving authors (as far as possible)
  4. I give a large number of tests (50-100) comprehensively testing understanding and demonstrate various combinations
  5. I give laboratory for independent work

This article follows points # 1 (various options) and # 2 (a sequence of examples for each option).

skype: GolovachCourses
email: GolovachCourses@gmail.com

Source: https://habr.com/ru/post/223821/


All Articles