Java 中的条件编译:编译器会从类中省略“始终为假”的块吗?
Posted
技术标签:
【中文标题】Java 中的条件编译:编译器会从类中省略“始终为假”的块吗?【英文标题】:Conditional compilation in Java: will compiler omit "always false" blocks from class? 【发布时间】:2012-06-11 19:02:31 【问题描述】:下面是我的代码的 sn-p:
class A
private boolean debug = false;
// Called when server boots up.
public void init (property)
debug = property.getBoolean ("debug_var"); // read debug from a config file.
// some other function
public void foo ()
if (debug)
System.out.println ("From inside the debug block");
当我运行代码时,如果(调试)实际上在配置文件中打印出“从调试块内部”如果 debug == true。
两个问题:
那么,在这种情况下,编译器是否将 if 块包含在 .class 文件中只是因为变量 debug 的值可能会在运行时发生变化?
如果这是真的,那么在某些环境中,如何避免将某些代码添加到 .class 文件中?
【问题讨论】:
与***.com/questions/1922521/…重复 【参考方案1】:如果你必须这样做,大多数日志框架都有自己设置日志详细级别的方法,它们只是不会在运行时输出任何级别太低的日志语句。使用日志框架来正确执行此操作。
例如,使用内置的 java.util.logging 框架,您可以执行类似的操作
Logger.getLogger("ThisClass").log(Level.FINE, "Log message");
仅在日志级别设置为 FINE 或更低时打印,但在日志级别为 CONFIG 或更低时被忽略。
“条件编译”通常在 Java 中没有意义,但需要注意的是,JIT 会优化掉它可以确定永远不会执行的分支。
【讨论】:
知道了。谢谢!你能看看我上面对戴夫牛顿的回答的评论吗? 是的。我不认为有办法做到这一点。【参考方案2】:最接近的方法是使用静态变量,它在运行时(基本上)是无用的。
但是,在您的示例中,JVM 可能会在运行足够多的时间后将其优化掉,因此如果您关心的是运行时效率,则可能不值得担心。
最后我会问你为什么要根据环境从 class 文件中删除代码 - 如果它不是可以在运行时更改的东西,那么你最好的选择是创建一些可以在运行时确定/注入的可插拔实现形式。
【讨论】:
我想要删除一些代码的原因是因为在开发环境中。我有一些我不想在生产中使用的失态。您能否详细说明一下您提到的开发可插拔实现,或者您能否指出一些好的资源。谢谢。 @FSP 好吧...我会修复代码或使其可配置。我看不出有任何真正的理由将其从 class 文件中删除,但您的情况尚不清楚。 “可插入”是指“在这个环境中,使用这个实现”——有很多方法可以做到这一点,大多数都围绕 some 形式的依赖注入/控制反转,所以我d首先瞄准那个。【参考方案3】:正如其他人所指出的,Java 编译器通常非常幼稚,只会将您拥有的 Java 源代码转换为 Java 字节码。例如,if
语句最常被转换为条件分支,无论您是否发现分支条件始终为假是显而易见的。
然而,从理论的角度来看,没有什么可以阻止编译器找出某些分支永远不会被采用,并在字节码中简单地省略它们。 Java 语言规范中没有规定如何在以字节码为目标时编译某些内容。
【讨论】:
【参考方案4】:-
是的,编译器会将其包含在 .class 文件中。
没有条件编译机制。您可以使用编译器插件(在 JDK 1.7 及更高版本中)来模拟其中的一部分,或者您可以尝试使用 AspectJ 等字节码操作技术来实现类似的效果。
【讨论】:
规范不保证 1 和 2。 (所以从技术上讲,答案是错误的。) 什么?不允许编译器删除可达代码。它可以在某些条件下优化掉条件,但打印语句必须在那里。那是在规范中!对于 2,语言规范中没有任何内容允许条件编译。那我的回答怎么错了?【参考方案5】:过去,如果您将布尔值声明为 static
和 final
并将其值设置为 false
,编译器将忽略代码。不过,那是最初的 Java 编译器。我不知道当前的是否这样做(当前的已经存在很长时间了)。这背后的基本原理是让您可以使您的小程序下载更小。它充其量只是一个笨拙的机制,因此没有被广泛使用。
要对此进行测试,请将您的调试变量更改为 private static final boolean debug=false;
编译它,保存类文件,然后将其翻转为 true。然后再编译一遍,看看class文件有没有不同。顺便说一句,当 debug 声明为 final 时,您的 init 函数将不起作用。
【讨论】:
【参考方案6】:没有什么能比得上条件编译(除非 java 找到一个保证不会被执行的条件)。您的类文件包含的字节码与您在 Java 文件中的任何内容相同。
编辑:正如 aiobee 所指出的,如果编译器发现分支语句不被执行,它可能会忽略它们。
【讨论】:
没有什么可以阻止编译器忽略分支,只要它们保证不被执行。 :-) 好吧,现在你的回答有点矛盾(或者你我对条件编译的定义不同。) 我认为这是定义“条件”的角度。以上是关于Java 中的条件编译:编译器会从类中省略“始终为假”的块吗?的主要内容,如果未能解决你的问题,请参考以下文章