A better example for IOException is network errors. If your socket dies, your code should attempt to reopen it! It should report to the user if it can't!
Moreover, it's good that code nearest the site where the exception is thrown handles the error, as only it has context for what's going on at the time. Code further up the stack won't have any clue what this random IOException might relate to.
If you're confident the IOExceptions can't occur under normal conditions -- say, you know the file has correct permissions, isn't being concurrently modified, etc. -- then, encode this belief by catching the IOException near to its origin and rethrowing as an unchecked exception.
This same pattern shows up even in languages without exceptions. In C -- always check errno; don't try to catch SIGSEGV or SIGABRT; raise SIGABRT if errno is something you don't plan to handle. In F# -- you're forced to match Ok/Error; don't try to catch exceptions; raise an exception if you don't expect Error.
But with abstractions you can have many more functions not handling the error before you get to the actual context.
Is it worth it to thread the IOException through the network layer, then the HTTP-client library, then then business serialisation up to the actual context?
I don't want a dozen checked exceptions. (and with this approach socket-limitations and connection loss should get a different exception.)
I also don't want catch and rethrow. Unchecked+checked+docs+crashes are a measured approach.
Check the root message of this thread... if you use unchecked exceptions you either crash or you swallow all exceptions. Both are evil, and checked exceptions are the solution.
That's only possible if your code is 100% correct.
Sometimes, your logic is flawed. A condition occurs which you erroneously deduced not to be possible.
Unchecked exceptions are the necessary manifestation of these unforeseen errors. Catching them is pointless, what will you do with them? Dynamically fix the logic of your program? What can be the reasonable response to "index out of bounds", try a different index? [1]
Depending on the domain it may be appropriate to convert them indiscriminately to checked exceptions at module boundaries (e.g. requests within a server) -- but within said module it remains pointless to catch them. (This is a form of swallowing.)
In other domains, crashing is the correct behavior. The code cannot proceed correctly, it must abort.
Checked exceptions are appropriate only when the exception can be anticipated and thus planned for, ideally by code closest to where it is thrown.
[1] Actually there was a paper a long time ago that monkey-patching C code to return fake "null" data in response to out-of-bounds memory accesses actually resulted in the intended (i.e. correct) behavior in the majority of cases. But I digress.
Some errors are recoverable, some are not. When recoverable errors are possible you want to know what those errors are. If you don't then you will crash when you could have recovered. And that's the reason for writing down the list of possible exceptions where recovery and retry are possible (aka checked exceptions).
When recovery should not be attempted (example: "index out of bounds") then you don't want to declare or catch the exception, and that's when you use a subclass of RuntimeException in Java.
https://phauer.com/2015/checked-exceptions-are-evil/