Loading...
Loading...
Java interview guide
Checked and unchecked exceptions, try-with-resources, files and robust error handling. Every answer is short enough to say aloud, and every question links to the lesson that teaches it.
61 questionsLast updated
Error and an Exception?An Error is a serious problem in the JVM, such as OutOfMemoryError or StackOverflowError, that a normal program cannot recover from. An Exception is a problem the program can expect and handle, such as a missing file or bad input. Programs catch exceptions and leave errors alone.
StackOverflowError an exception?In everyday speech people call it one, but in the class tree it is an Error, under VirtualMachineError. So catch (Exception e) does not catch it. The real fix is the recursion that never stops.
instanceof check for RuntimeException come before a check for Exception?Because every RuntimeException is also an Exception. If the Exception check runs first, it is true for runtime exceptions as well, so the more specific check is never reached. Always test the child class before the parent.
Error checked or unchecked?Unchecked. The compiler never asks you to catch or declare an Error, because a program normally cannot recover from one, such as OutOfMemoryError.
I catch it where I can do something useful: show a message, use a default, skip that item or try again. If this method has no good answer, I add throws IOException and let a caller that knows the situation handle it. I never add an empty catch just to make the error go away.
Java runs only the first catch whose type matches. If catch (Exception e) comes first, it takes every exception and the specific block below can never run, so the compiler reports "exception X has already been caught".
It swallows the exception: the problem happens, but nobody sees it. The program continues with missing or wrong data, and the bug shows up later, far from its cause. If you catch, report it, count it, use a named default or rethrow.
Lesson: What to catch and what to leave alonecatch (Exception e) always wrong?No. At the top of a program, such as the main loop of a server or the code that handles one request, a final catch (Exception e) that logs the problem and keeps the server running is reasonable. Deep inside business logic it usually hides bugs.
finally always run?It runs on every normal path: normal end, caught or uncaught exception, return, break and continue. It does not run if System.exit is called, or if the JVM process dies, for example when it is killed or crashes.
try block and the finally block throw an exception?The exception from finally replaces the one from try, so the caller sees only the clean-up problem and the real cause is lost. To avoid that, wrap risky clean-up in its own try-catch inside finally, or use try-with-resources, which keeps the second exception as a suppressed one.
Try first, until it ends or throws. Then the first matching catch, only if there was an exception. Then finally, always. Then either the code after the statement, or, if a return or an uncaught exception is pending, the method leaves and the caller continues.
Use the constructor that takes a message and a Throwable: throw new IllegalStateException("sync failed", e);. For your own exception class, add a constructor (String message, Throwable cause) that calls super(message, cause).
throw new RuntimeException(e.getMessage()); inside a catch. What would you say in review?That it loses the original exception: its type and its stack trace are gone, and only the text survives. I would ask for throw new RuntimeException("what failed here", e);, which keeps the full original as the cause.
new RuntimeException(cause) use as its message?It uses cause.toString(), which is the cause's class name, a colon and its message, for example java.lang.NumberFormatException: For input string: "9O". Usually a message of your own plus the cause is clearer.
java.base?Because JDK code like Integer.parseInt is almost never the bug; it is reporting that it got a bad value. The first frame from my own code shows where that bad value was passed in, which is what I can fix.
e.printStackTrace() and e.printStackTrace(System.out)?The first prints to standard error (System.err), the second to standard output. Because they are separate streams, a trace sent to System.err can appear out of order with normal output. Real applications usually send traces to a log instead of printing them.
Rethrowing with throw e; sends the same exception object on, so the type and stack trace do not change. Wrapping creates a new exception, often of a different type such as an unchecked one, and keeps the original as its cause.
UncheckedIOException and when would you use it?It is an unchecked exception from Java 8 whose cause is always an IOException. I use it when I/O code runs inside something that cannot throw checked exceptions, such as a stream lambda or a Comparator. getCause() gives the original IOException back.
IOException inside a stream but still give your callers an IOException?I call a small helper inside the stream that wraps the IOException in an UncheckedIOException. Around the stream I catch UncheckedIOException and throw e.getCause();, so the public method keeps throws IOException and callers never see the wrapper.
The constructor calls fillInStackTrace(), which walks the call stack and stores every frame. The deeper the stack, the more work. It happens at new, so even an exception that is never thrown pays the cost.
NumberFormatException when a user types a number?No. One failed parse per user action costs almost nothing, and try first is the clearest code there. The problem is only when the exception path runs thousands of times as a normal part of processing.
Lesson: Why exceptions are not for normal flowLooping with while (true) over an array and catching ArrayIndexOutOfBoundsException to stop. The fix is a normal loop condition, for (int i = 0; i < a.length; i++). Another example is throwing an exception to leave nested loops; the fix is to move the loops into a method and return.
finally block be skipped?Only if the JVM stops: System.exit is called, the process is killed, or the JVM crashes. On every normal path, including return, break, continue and uncaught exceptions, finally runs.
It is wrapping a caught exception in a new one, with the original as its cause: new ImportException("cannot import row", e). The caller gets a message at its own level, and the trace still shows the original under Caused by:, so the real reason is never lost.
A resource leak is a resource that was opened but never closed. The program usually keeps working until the leaks reach a limit, such as the OS limit on open files or the database's connection limit, and then new opens fail.
Lesson: Resources and why they must be closedIt means the process reached the OS limit on open handles, which almost always points to a leak. I would look for opens whose close() can be skipped by an early return, a break or an exception, and check the count of open files over time with an OS tool such as lsof on Linux.
StringReader needed? It holds no file.Closing it frees nothing important, because it only reads a String in memory. Still, the safe habit is to close anything that is AutoCloseable, because code that works with a Reader cannot know whether it reads a string or a real file.
close() in some old code?When the open happens inside the try, the variable is declared outside as null. If the open fails, finally still runs and close() on null throws a NullPointerException, which hides the real error. The if (x != null) check prevents that.
In reverse order of opening. The second resource often depends on the first, like a writer that writes data read from a reader, so it should finish first. Nested try-finally gives this order naturally.
It is a try statement, added in Java 7, that declares resources in brackets after try. Java closes each resource automatically when the block ends, whether it ends normally, returns or throws, so you do not write a finally for closing.
close() called, compared with catch and finally?Right after the body ends, before any catch or finally block. So inside catch the resource is already closed, and the variable is not even in scope there.
close() method in the brackets?No. The type must implement AutoCloseable; the compiler checks the interface, not the method name. Otherwise it reports "try-with-resources not applicable to variable type".
Separate the declarations with semicolons in the same brackets, for example try (BufferedReader in = ...; BufferedWriter out = ...). Later declarations can use the earlier variables.
try (BufferedReader br = new BufferedReader(new FileReader(f))) safe?Mostly, but not fully. If the BufferedReader constructor throws, the FileReader is already open and nothing closes it. Declaring both, inner first, closes the inner reader in every case.
BufferedWriter end up empty?Because the text waits in the buffer until it is flushed, and closing is what flushes it. If only the inner writer is declared as a resource, the BufferedWriter is never closed and its text never reaches the file.
Because the body's exception is usually the real cause, and the close failure is often a side effect of it. Old finally code let the close failure replace the cause, which made bugs very hard to trace.
A cause explains why this exception happened, for example an IOException wrapped in a ReportException. A suppressed exception is a separate failure that happened while cleaning up. Stack traces show them as Caused by: and Suppressed:.
null?No. e.addSuppressed(e) throws IllegalArgumentException with "Self-suppression not permitted", and addSuppressed(null) throws NullPointerException.
close() be idempotent?Because the same object is often closed more than once, for example by a wrapper and then directly, or by hand and then by try-with-resources. If the second call does work again, counters go wrong or the second release fails. A closed flag that returns early solves it.
Start the work in the constructor, give the class a commit() method that marks success, and in close() roll back if commit() was never called. Then try (Tx tx = new Tx()) { ...; tx.commit(); } rolls back automatically on any exception.
finally still needed now that we have try-with-resources?Yes, for clean-up that is not a close() call. Releasing a ReentrantLock with unlock(), resetting a flag or restoring a setting still belong in finally. For objects that are AutoCloseable, try-with-resources is the better choice.
In small steps: move each open into the brackets in the same order, delete the close() calls and null checks, merge the nested try blocks, and keep any catch blocks after the new try. Then I run the tests, because the only behaviour change should be better exception reporting.
lock() called before the try and not inside it?If lock() were inside the try and failed, finally would call unlock() on a lock this thread does not hold, which throws IllegalMonitorStateException and hides the real error. With lock() before the try, finally only runs once the lock is held.
I look for every open whose close() is not guaranteed: a close() at the end of a method, inside an if, or after a call that can throw. Each one becomes a try-with-resources. On a running system I also watch the count of open files or connections over time.
By logging the whole exception object, not just getMessage(). A logger or printStackTrace prints the Caused by: and Suppressed: blocks, which often hold the real reason.
With a small group class, or the same loop it uses: close in reverse order of opening, catch each failure, keep going, then throw the first failure with the others added by addSuppressed. That copies what try-with-resources does for a fixed number of resources.
Path.of and Paths.get?They do the same thing. Paths.get came with Java 7, and Path.of was added in Java 11 as a static method on the interface itself. In new code I use Path.of; Paths.get still works and appears in older code.
getParent() return null, and why does it matter?It returns null when the path has only one name, such as marks.txt, and for a bare root. If I chain p.getParent().getFileName() without checking, I get a NullPointerException, so I check the parent first or use toAbsolutePath() when I really need the folder.
Path.of("data", "marks.txt") produce the same text on every computer?No. Java joins the parts with the system separator, so toString() gives data\marks.txt on Windows and data/marks.txt on macOS and Linux. That is exactly why building paths from parts is safer than typing the separator yourself.
user.dir?It is the system property that holds the working directory, the folder the JVM was started from. Relative paths are resolved against it, and Path.of("").toAbsolutePath() gives the same folder.
normalize() check whether the folders exist?No. It only works on the text of the path, removing . and cancelling a name with a following ... So missing/../marks.txt becomes marks.txt even if missing does not exist; that is fine for most uses, but it can differ from the real file system when symbolic links are involved.
"data\\marks.txt" in Java code?It hard-codes the Windows separator. On Linux a backslash is an ordinary character, so the path becomes one strange file name. I write Path.of("data", "marks.txt"), and Java adds the right separator.
Config.json on Windows but not on our Linux server. Why?Linux file names are case-sensitive, while Windows ignores case when looking up a file. The real name probably differs only in case, like config.json. I use the exact name everywhere, and a lower-case naming habit helps.
C:\Users\Ravi\Desktop in an application?I take the location from outside the code: a command-line argument, a settings file or user.home, with a sensible relative default. Then the same program runs on any machine, including a Linux server or a CI build.
createDirectory and createDirectories?createDirectory makes one folder and fails if its parent is missing or the name already exists. createDirectories makes every missing folder on the way and does nothing if the folder is already there, so it is safe to call at every start-up.
if (!Files.exists(p)) Files.createFile(p); not fully safe?Another program or thread can create the file between the check and the create, which is called a check-then-act race. createFile already checks and creates in one step, so I just call it and catch FileAlreadyExistsException.
Files.notExists(p) the same as !Files.exists(p)?Not always. If Java cannot find out, for example because a folder on the way denies access, both methods return false. So !exists means "not confirmed to exist", while notExists means "confirmed missing".
Files.copy do by default if the target exists, and how do you change that?It throws FileAlreadyExistsException and changes nothing, so you never lose data by accident. If overwriting is really wanted, I pass StandardCopyOption.REPLACE_EXISTING.
ATOMIC_MOVE fail with AtomicMoveNotSupportedException?Because the source and target are on different file stores, like C: and D: or a pen drive. Moving between them needs a copy and a delete, which cannot be one step. I catch that exception and fall back to a normal move if that is acceptable.
COPY_ATTRIBUTES?When the copy should look like the original, for example a backup tool that compares last-modified times to decide what changed. Without it, Linux and macOS give the copy the current time, and the tool would think every file changed.
Lesson: Copy and move files with optionsFiles.size return a long and not an int?An int can count only up to about 2.1 billion, roughly 2 GB. Videos, disk images and logs are often bigger, so the size needs a long.
Each lesson has quizzes, practice and more interview questions on the same topic.
Open the first lesson