Experienced interview questions
Checked exceptions, such as IOException, must either be declared in the throws clause of a method or caught in a try-catch block, and the compiler enforces this at compile time. They generally represent conditions a caller could reasonably be expected to recover from, such as a missing file or a failed network call. Unchecked exceptions extend RuntimeException, are not enforced by the compiler, and typically signal programming mistakes, like dereferencing a null reference or passing an invalid argument.
In practice, many experienced Java developers lean toward unchecked exceptions for their own application errors, reserving checked exceptions for genuinely recoverable situations, because forcing every caller up the chain to declare or catch an exception it cannot meaningfully act on tends to clutter code with boilerplate rather than improve reliability. Spring itself follows this philosophy, favouring unchecked exceptions throughout its own APIs.
The stack holds each method call as a frame containing its local variables and parameters, and it is strictly short-lived: as soon as a method returns, its frame and everything in it disappears. The heap, in contrast, holds actual objects, and those objects live for as long as something still references them; once nothing points to an object any more, the garbage collector eventually reclaims that memory.
A useful way to picture this is that a local variable on the stack can hold a reference, essentially an address, pointing to an object that physically lives on the heap. So when the line Car myCar = new Car() runs, the reference myCar sits on the stack for the lifetime of that method, while the actual Car object it points to sits on the heap and can keep living even after the method that created it returns, as long as something else still references it, for example if it was returned or stored in a list.