1. 概述

在这个教程中,我们将讨论Java规范(Java规范,第14.22节)中的一项内容:编译器应该在遇到任何不可达语句时抛出错误。不可达语句是指程序执行过程中永远不会被执行的代码,因为程序流程无法到达它。我们将通过各种代码示例来说明这个定义。

2. 循环后的break指令

在循环中,如果我们在break语句后放置指令,这些指令是不可达的:

public class UnreachableStatement {
    
    public static void main(String[] args) {
        for (int i=0; i<10; i++) {
            break;
            int j = 0;
        }
    }

}

尝试用javac编译我们的代码:

$ javac UnreachableStatement.java
UnreachableStatement.java:9: error: unreachable statement
            int j = 0;
                ^
1 error

正如预期,编译失败,因为int j = 0;语句不可达。类似地,循环中的continue语句后的指令也是不可达的:

public static void main(String[] args) {
    int i = 0;
    while (i<5) {
        i++;
        continue;
        int j = 0;
    }
}

3. while(true)后的代码

一个while(true)语句意味着其内部代码会永远运行。因此,其后的任何代码都是不可达的:

public static void main(String[] args) {
    while (true) {}
    int j = 0;
}

再次,前一个代码中的int j = 0;语句不可达。这个评论同样适用于使用do-while结构的等效代码:

public static void main(String[] args) {
    do {} while (true);
    int j = 0;
}

另一方面,while(false)循环内的任何代码都是不可达的:

public static void main(String[] args) {
    while (false) {
        int j = 0;
    }
}

4. 方法返回后的代码

方法在return语句后立即退出。因此,此语句之后的任何代码都是不可达的:

public static void main(String[] args) {
    return;
    int i = 0;
}

又一次,int j = 0;行不可达,导致编译错误。同样,当throw语句不被try-catch块包围或在throws子句中指定时,方法会以异常方式完成。因此,此行之后的任何代码都不可达:

public static void main(String[] args) throws Exception {
    throw new Exception();
    int i = 0;
}

总结来说,如果所有代码分支都返回,那么以下代码无论如何都无法到达:

public static void main(String[] args) throws Exception {
    int i = new Random().nextInt(0, 10);
    if (i > 5) {
        return;
    } else {
        throw new Exception();
    }
    int j = 0;
}

在这个例子中,我们选择了一个随机数在0(包括)和10(不包括)之间。如果这个数字大于5,我们会立即返回;否则,我们抛出一个通用的Exception。因此,if-else块后的代码没有可能的执行路径。

5. 死但可达到的代码

最后,值得注意的是,即使明显的死代码也不一定从编译器的角度说是不可达的。特别是,它不会评估if语句内的条件:

public static void main(String[] args) {
    if (false) {
        return;
    }
}

即使一眼就能看出if块内的代码是死代码,这段代码也能成功编译。

6. 总结

在这篇文章中,我们探讨了多种不可达语句的情况。开发者社区对是否应将不可达代码视为警告还是错误存在争议。Java语言遵循每行代码都应该有目的的原则,因此选择抛出错误。而在像C++这样的其他语言中,由于编译器可以执行尽管存在不一致性,它通常只会发出警告。