3

下面是我的代码片段:

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");
       }
   }
 }

当我运行代码时, if (debug) 实际上在配置文件中打印出“从内部调试块”如果 debug == true。

两个问题:

  1. 那么,在这种情况下,编译器是否将 if 块包含在 .class 文件中只是因为变量 debug 的值可能会在运行时发生变化?

  2. 如果这是真的,那么如何在某些环境中消除将某些代码添加到 .class 文件中?

4

6 回答 6

5

如果你必须做这样的事情,大多数日志框架都有自己设置日志详细级别的方法,它们只是不会在运行时输出任何级别太低的日志语句。使用日志框架来正确执行此操作。

例如,使用内置的 java.util.logging 框架,您可以执行类似的操作

Logger.getLogger("ThisClass").log(Level.FINE, "Log message");

只有当日志级别设置为 FINE 或更低时才会打印,但当日志级别为 CONFIG 或更低时会被忽略。

一般来说,“条件编译”在 Java 中没有意义,但需要注意的是,JIT 会优化掉它可以确定永远不会执行的分支。

于 2012-06-11T19:06:12.827 回答
1

正如其他人所指出的那样,Java 编译器通常非常幼稚,只是将您拥有的 Java 源代码转换为 Java 字节码。例如,一条if语句最常被转换为条件分支,无论您是否发现分支条件总是为假是显而易见的。

然而,从理论的角度来看,没有什么可以阻止编译器找出某些分支永远不会被采用,并在字节码中简单地省略它们。Java 语言规范中没有任何内容规定在以字节码为目标时应如何编译某些内容。

于 2012-06-11T19:11:07.143 回答
1

最接近的方法是使用静态变量,这在运行时(基本上)是无用的。

但是,在您的示例中,JVM 可能会在运行足够多的时间后将其优化掉,因此如果您关心的是运行时效率,则可能不值得担心。

最终我会问你为什么要根据环境从文件中删除代码——如果它不是可以在运行时改变的东西,那么你最好的选择是创建某种形式的可以确定/注入的可插入实现在运行时。

于 2012-06-11T19:11:25.360 回答
0

没有像条件编译这样的东西(除非 java 找到一个保证不会执行的条件)。您的类文件包含的字节码与您在 Java 文件中的任何内容相同。

编辑:正如 aiobee 所指出的,如果编译器发现分支语句不被执行,它可能会省略它们。

于 2012-06-11T19:06:02.373 回答
0
  1. 是的,编译器会将其包含在 .class 文件中。
  2. 没有条件编译的机制。您可以通过使用编译器插件(在 JDK 1.7 和更高版本中)来模拟其中的一些,或者您可以尝试通过使用像 AspectJ 这样的字节码操作技术来实现类似的效果。
于 2012-06-11T19:07:50.320 回答
0

过去,如果您将布尔值声明为staticandfinal并将其值设置为false,编译器将忽略代码。不过,那是最初的 Java 编译器。我不知道当前的是否这样做(当前的已经存在很长时间了)。这背后的基本原理是让您可以使您的小程序下载更小。它充其量只是一个笨拙的机制,因此没有被广泛使用。

要对此进行测试,请更改您的调试变量以对其进行private static final boolean debug=false;编译,保存类文件,然后将其翻转为 true。然后再编译一遍,看看class文件有没有不同。顺便说一句,当 debug 声明为 final 时,您的 init 函数将不起作用。

于 2012-06-11T19:09:43.393 回答