How do the try, catch and finally blocks interact?
They interact in a specific order, and knowing the order answers most of the questions people ask about them.
The try block runs first. If it completes without throwing, the catch block is skipped entirely and control moves to the finally block, which always runs. If the try block throws, the catch block runs with the thrown value, and then the finally block runs as well. The finally block runs on every path through the try statement, whether the code succeeded, threw, or returned early.
Two important details. A return inside the try block does not skip the finally block. The return value is calculated, but the finally block still runs before the function actually returns, and if the finally block returns or throws, that wins. And the catch block is optional. A try with a finally and no catch is valid, and it is the right shape for cleanup that has to happen whether or not something went wrong.
There is also the question of what the finally block can do to the outcome. It can swallow an error just by returning normally, which turns a thrown exception into a normal return, and that is worth using deliberately when a failure really is expected and recoverable. It can also replace an error with a different one by throwing from inside it, which is occasionally useful for translating a low level failure into a domain specific one.