109

我怀疑是否有办法在 Java 中创建编译时条件,例如 C++ 中的#ifdef #ifndef。

我的问题是有一个用 Java 编写的算法,并且我对该算法有不同的运行时间改进。所以我想衡量每次使用改进时我节省了多少时间。

现在我有一组布尔变量,用于在运行期间决定应该使用哪些改进,哪些不应该使用。但即使测试这些变量也会影响总运行时间。

所以我想找到一种方法来决定在编译期间应该编译和使用程序的哪些部分。

有人知道用Java做的方法吗?或者也许有人知道没有这样的方法(它也很有用)。

4

9 回答 9

129
private static final boolean enableFast = false;

// ...
if (enableFast) {
  // This is removed at compile time
}

像上面显示的条件是在编译时评估的。相反,如果您使用它

private static final boolean enableFast = "true".equals(System.getProperty("fast"));

然后,JIT 编译器将评估任何依赖于 enableFast 的条件。这样做的开销可以忽略不计。

于 2009-11-28T21:50:48.013 回答
46

javac 不会输出无法访问的编译代码。为您使用设置为常量值的最终变量#defineif#ifdef.

您可以使用 javap 来证明无法访问的代码不包含在输出类文件中。例如,考虑以下代码:

public class Test
{
   private static final boolean debug = false;

   public static void main(String[] args)
   {
       if (debug) 
       {
           System.out.println("debug was enabled");
       }
       else
       {
           System.out.println("debug was not enabled");
       }
   }
}

javap -c Test给出以下输出,表明只有两个路径之一被编译(并且 if 语句没有):

public static void main(java.lang.String[]);
  Code:
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc     #3; //String debug was not enabled
   5:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   return
于 2009-11-28T21:49:44.220 回答
12

我想我已经找到了解决方案,它要简单得多。
如果我用“final”修饰符定义布尔变量,Java 编译器本身就可以解决问题。因为它提前知道测试这个条件的结果是什么。例如这段代码:

    boolean flag1 = true;
    boolean flag2 = false;
    int j=0;
    for(int i=0;i<1000000000;i++){
        if(flag1)
            if(flag2)
                j++;
            else
                j++;
        else
            if(flag2)
                j++;
            else
                j++;
    }

在我的电脑上运行大约 3 秒。
和这个

    final boolean flag1 = true;
    final boolean flag2 = false;
    int j=0;
    for(int i=0;i<1000000000;i++){
        if(flag1)
            if(flag2)
                j++;
            else
                j++;
        else
            if(flag2)
                j++;
            else
                j++;
    }

运行约 1 秒。这段代码需要的同时

    int j=0;
    for(int i=0;i<1000000000;i++){
        j++;
    }
于 2009-11-28T22:16:08.390 回答
2

没用过,但是有这个

JCPP 是 C 预处理器的完整、兼容、独立、纯 Java 实现。它适用于使用 sablecc、antlr、JLex、CUP 等工具在 Java 中编写 C 风格编译器的人。该项目已用于成功预处理 GNU C 库的大部分源代码。从 1.2.5 版本开始,它还可以预处理 Apple Objective C 库。

http://www.anarres.org/projects/jcpp/

于 2009-11-28T21:40:53.890 回答
2

如果您确实需要条件编译并且使用Ant,则可以过滤您的代码并在其中进行搜索和替换。

例如:http ://weblogs.java.net/blog/schaefa/archive/2005/01/how_to_do_condi.html

以同样的方式,例如,您可以编写一个过滤器来替换LOG.debug(...);/*LOG.debug(...);*/. 这仍然会比if (LOG.isDebugEnabled()) { ... }东西执行得更快,更不用说同时更简洁了。

如果您使用Maven ,这里描述了一个类似的特性。

于 2012-07-13T11:01:31.257 回答
2

Manifold提供了一个完全集成的 Java 预处理器(没有构建步骤或生成的源代码)。它专门针对条件编译并使用 C 风格的指令。

Manifold 的 Java 预处理器

于 2019-07-30T00:02:56.500 回答
2

如果您使用 IntelliJ,则有一个名为 Manifold 的插件,它与许多其他功能一起允许在 Java 中#ifdef使用#define

插件网址: https ://manifold.systems/

预处理器信息: https ://github.com/manifold-systems/manifold/tree/master/manifold-deps-parent/manifold-preprocessor

PS:我不隶属于他们,我们只是碰巧使用它,它在没有工作流的情况下有很大帮助(这对于 Java 开发来说可能不是典型的)

于 2020-12-17T15:55:36.753 回答
1

使用工厂模式在类的实现之间切换?

对象创建时间现在不是问题了吗?当在较长的运行时间段内平均时,花费时间的最大组成部分现在应该在主算法中,不是吗?

严格来说,你并不真的需要一个预处理器来完成你想要实现的目标。当然,除了我提出的方法之外,很可能还有其他方法可以满足您的要求。

于 2009-11-28T21:41:36.723 回答
0
final static int appFlags = context.getApplicationInfo().flags;
final static boolean isDebug = (appFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0
于 2015-09-21T12:45:22.290 回答