Unreachable code in Java is a statement that no execution path can reach, and the compiler javac rejects it with the error “unreachable statement”. Dead code is code that compiles but can never run, such as the lines after if (true) return;, so javac accepts it and Eclipse marks it with a “Dead code” warning.
We meet both when we leave a log line after a return or switch off a block of code with a constant flag. The difference between the two is also a common puzzle in Java interviews.
The following example is the puzzle. Each of the three methods prints “saved” and leaves the method before it prints “sent”, and the comment on each last line shows what javac in Java 25 says about it.
// does not compile: first() and third() contain an unreachable statement
void first() {
System.out.println("saved");
return;
System.out.println("sent"); // error: unreachable statement
}
void second() {
System.out.println("saved");
if (true) {
return;
}
System.out.println("sent"); // compiles (dead code, Eclipse warning)
}
void third() {
System.out.println("saved");
while (true) {
return;
}
System.out.println("sent"); // error: unreachable statement
}
Notice that the line “sent” can never run in any of the three methods, but only second() compiles. In the next sections, we look at the rule in the Java Language Specification that explains the difference, list the statements that make the next line unreachable and fix each case.
1. Unreachable Code vs Dead Code
The two terms describe code that never runs, but they differ in who reports the problem and whether the class compiles.
- Unreachable code breaks a rule of the language. The Java Language Specification (JLS) requires every statement to be reachable, so javac stops with a compile error, and every Java compiler must do the same.
- Dead code follows the language rules, so the class compiles. The code can still never run, and IDEs and code analysis tools report it as a warning, which Eclipse calls “Dead code”.
The table sums up the puzzle from the intro, with the result of javac and of the Eclipse compiler for each method.
| Method | Last statement | javac result | Eclipse compiler result |
|---|---|---|---|
| first() | after return; | error, unreachable statement | error, Unreachable code |
| second() | after if (true) { return; } | compiles | warning, Dead code |
| third() | after while (true) { return; } | error, unreachable statement | error, Unreachable code |
Dead code is still a bug in most cases. A developer who added the “sent” line to second() expected it to run, and javac compiles the class without any message, so only a test or a code review finds the mistake.
2. Why if (true) Compiles but while (true) Does Not
The JLS defines reachability with a fixed set of rules (JLS 14.22). The compiler follows the structure of the statements and does not evaluate conditions, with one exception. For while, do and for loops, the compiler checks whether the condition is a constant expression with the value true.
So for third(), the compiler sees while (true), a loop that can only end with a break. The loop body has no break, so the statement after the loop is unreachable. For second(), the rule for if ignores the condition, so the compiler assumes that the if body may or may not run, and the statement after the if is reachable.

2.1. Conditional Compilation With a Constant Flag
The JLS treats if differently on purpose. The rule lets us switch code on and off with a constant flag, which the JLS calls conditional compilation. For example, a payment service prints debug details while we develop it, and we turn the output off for production by changing one constant.
void pay() {
if (DEBUG) {
System.out.println("debug: card checked"); // never runs, compiles
}
System.out.println("paid"); // paid
}
static final boolean DEBUG = false;
If the if rule worked like the while rule, the DEBUG block would be a compile error. When the condition is a constant false, javac leaves the if body out of the bytecode, so the debug code costs nothing at runtime. We can check it with javap -c, which prints the bytecode of pay() with only the “paid” call in it.
void pay();
Code:
0: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #15 // String paid
5: invokevirtual #17 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
The Eclipse compiler accepts the pattern as well. It reports “Dead code” for if (true) { return; } and if (false), but not for a condition that is a named constant such as DEBUG.
3. Statements That Make the Next Line Unreachable
A statement after return, throw, break, continue or yield in the same block is always unreachable, because these statements never complete normally. A loop with a constant true condition and no break also never completes, so the statement after it is unreachable too.
The left column shows the code before a statement, and the right column shows what javac 25 reports for the statement that follows.
| Code before the statement | Result |
|---|---|
| return; | error, unreachable statement |
| throw new IllegalStateException(“payment declined”); | error, unreachable statement |
| break; or continue; inside a loop | error, unreachable statement |
| for (;;) { … } without break | error, unreachable statement |
| while (true) { … } without break | error, unreachable statement |
| if (paid) { return; } else { return; } | error, unreachable statement |
| while (false) { … } (the loop body itself) | error, unreachable statement |
| final boolean running = true; while (running) { … } | error, unreachable statement |
| boolean running = true; while (running) { … } | compiles |
| while (true) { … break; … } | compiles |
| System.exit(0); | compiles |
Two rows surprise many developers. A final local variable with a constant value is a constant variable, so while (running) is the same as while (true) for the compiler. Without final, the condition is no longer a constant, and the code after the loop compiles.
The System.exit(0) row shows the limit of the analysis. The compiler does not look into methods, so it cannot know that System.exit() never returns, and the next line compiles even though it never runs.
3.1. Loops That Need a break
A background job that polls a queue often runs in a while (true) loop. The statement after the loop compiles only when a break can leave the loop, and a labeled break works for nested loops as well.
int attempts = 0;
while (true) {
attempts++;
if (attempts == 3) {
break;
}
}
int stoppedAfter = attempts; // 3
The same rule has a useful side effect. A method that returns a value from inside a while (true) loop needs no return statement after the loop, because the end of the method is unreachable.
static int nextTicket() {
int ticket = 0;
while (true) {
ticket++;
if (ticket > 3) {
return ticket;
}
}
}
int ticket = nextTicket(); // 4
3.2. Unreachable catch Blocks
The compiler applies similar checks to catch blocks. A catch block for a checked exception that the try block cannot throw is a compile error, and so is a catch block that a broader catch before it already covers.
// does not compile: both catch blocks for IOException are unreachable
try {
System.out.println("saved");
} catch (IOException e) {
System.out.println("never thrown");
}
try {
new FileReader("orders.csv").close();
} catch (Exception e) {
System.out.println("any error");
} catch (IOException e) {
System.out.println("already caught");
}
error: exception IOException is never thrown in body of corresponding try statement
error: exception IOException has already been caught
To fix the second error, we put the more specific catch block first, because the catch clauses of a try-catch-finally statement are checked from top to bottom.
4. How to Fix Unreachable Code
The error message points at the first unreachable statement. The fix depends on what the code was supposed to do.
- If the statement must run, we move it before the return, throw or break. A log line such as “sent” goes before the return.
- If the statement must run even when the method returns or throws, we put it in a finally block.
- If the statement is left over from an old version, we delete it. Version control keeps the history, so commented-out code is not needed either.
- If a loop must end, we add a break or a real condition, such as while (!stopped), instead of while (true).
For example, an order service saves an order and sends a confirmation in a finally block, so the confirmation runs whether the save code returns normally or throws an exception.
static void placeOrder() {
try {
System.out.println("saved"); // saved
return;
} finally {
System.out.println("sent"); // sent
}
}
For dead code, the fix is the same, but javac does not help us find it. We turn on the dead code warning in the IDE, or run a static analysis tool in the build, so that an if (true) left over from debugging does not reach production.
5. Dead Code and Unreachable Code FAQs
The javac error and the IDE warning look alike, so most questions are about which one we get and why.
5.1. Is Unreachable Code an Error or a Warning in Java?
Unreachable code is an error. The JLS makes it a compile-time error, so every Java compiler, including javac and the Eclipse compiler, rejects it. Dead code, such as the code after if (true) return;, is only a warning in IDEs, and javac reports nothing, even with -Xlint:all.
5.2. Why Does if (false) Compile but while (false) Does Not?
The JLS allows if (false) on purpose, so that we can use constant flags for conditional compilation, as section 2.1 shows. For while (false), the JLS states that the loop body is reachable only when the condition is not a constant false, so the body is an unreachable statement.
5.3. Does Dead Code Slow Down the Program?
No, in most cases. For an if with a constant condition, javac leaves the dead branch out of the bytecode. Other dead code, such as an unused private method, stays in the class file and makes it bigger, but the code never runs, so it costs no CPU time.
5.4. Does the Compiler Find Every Line That Never Runs?
No. The compiler checks only the structure of the code and constant conditions. It does not know that System.exit() never returns or that a variable is always true, so such dead code compiles without an error.
5.5. When Does a Method Need a return After a Loop?
Only when the loop can complete normally. A method with a return type gets the error “missing return statement” when its end is reachable. After a while (true) loop without break, the end of the method is unreachable, so nextTicket() in section 3.1 compiles without a final return. A loop such as while (ticket < 10) can end, so the method needs a return after it.
6. Conclusion
Unreachable code and dead code both describe statements that never run. Unreachable code breaks the reachability rules of the JLS, so javac stops with “unreachable statement”. Dead code follows the rules, compiles and shows up only as an IDE warning.
The answer to the puzzle is one rule. The compiler treats while (true) as a loop that never ends without a break, but it never looks at the condition of an if. The JLS keeps the if rule on purpose, so that a constant DEBUG flag can switch code off and javac can drop that code from the bytecode.
To fix unreachable code, we move the statement before the return or throw, or put it into a finally block when it must always run. Dead code needs the same fixes, but we find it with IDE warnings and static analysis, because javac reports nothing.
7. References
- JLS 14.22 Unreachable Statements (Java SE 25)
- JLS 15.29 Constant Expressions (Java SE 25)
- JLS 11.2.3 Exception Checking (Java SE 25)
- javap command (Java 25)
Happy Learning !!
Difference b/w dead code & unreachable code?
still, I have confusion not clear with your examples.
Please share the specific question, what is not clear?
Still I have doubt could pls explain with some other example..
As per the java specification quote(i.e specified by you), ‘IF condition expression’ aren’t taken into account during compilation but when i was compiling the below code then it was giving me an warning ‘Dead code’ for line number 2.
if(1==2)
System.out.println(“Dead code”);
As per the quote, it should not give the warning.Please let me know that why this above code is giving an warning.
it might be happen that i could not understand this quote correctly.
As I told above “The unreachable code in method 2 is called “Dead code”. This is purely Eclipse compiler reported error”.
Try doing the same with javac, you won’t get error