题 Java中的#ifdef #ifndef


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

我的问题是有一个用Java编写的算法,并且我有不同的运行时间改进了该算法。因此,我想测量每次使用每次改进时节省的时间。

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

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

有人知道在Java中使用它的方法。或者也许有人知道没有这种方式(它也会有用)。


98
2017-11-28 21:38


起源




答案:


private static final boolean enableFast = false;

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

如上所示的条件在编译时进行评估。相反,如果你使用它

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

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


117
2017-11-28 21:50



这个解决方案比我的好。当我尝试使用预设的外部值初始化变量时,运行时间回到3秒。但是当我将变量定义为静态类变量(而不是函数局部变量)时,运行时间返回到1秒。谢谢您的帮助。 - jutky
IIRC,这在Java拥有JIT编译器之前甚至有效。该代码已删除 javac 我认为。这只适用于(比方说)的表达式 enableFast 是一个编译时常量表达式。 - Stephen C
是的,但是这个条件必须位于一个方法中,对吗?那么我们想要设置一堆私有静态最终字符串的情况呢? (例如,为生产与登台设置不同的一组服务器URL) - tomwhipple
@tomwhipple:是的,加上这不允许你做类似的事情: private void foo(#ifdef DEBUG DebugClass obj #else ReleaseClass obj #endif ) - Zonko
那么进口呢(例如,关于类路径)? - n611x007


javac不会输出无法访问的编译代码。使用最终变量设置为您的常量值 #define 和正常的 if 声明 #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

41
2017-11-28 21:49



这个javac是特定的,还是这个行为实际上是由JLS保证的? - Pacerier
@pacerier,我不知道这是否得到了JLS的保证,但是对于我自90年代以来遇到的每个java编译器都是如此,可能除了1.1.7之外,并且只是因为我没有然后测试一下。
顺便说一句,这个答案应该是公认的答案。


我认为我找到了解决方案,它更加简单。
如果我使用“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++;
    }

11
2017-11-28 22:16



这太有趣了。听起来JIT已经支持条件编译了!如果那些决赛在另一个级别或另一个包中,它会起作用吗? - joeytwiddle
@joeytwiddle是的,它始终有效。 - jutky
大!然后我相信这必须是运行时优化,代码实际上并没有在编译时被剥离。只要您使用成熟的VM,那就没问题了。 - joeytwiddle
@joeytwiddle,关键字是“只要你使用”一个成熟的虚拟机。 - Pacerier


从未使用它,但这存在

JCPP是完整的,合规的,   独立的纯Java实现   C预处理器。它的目的是   对于写C风格的人有用   Java中的编译器使用类似的工具   sablecc,antlr,JLex,CUP等   向前。该项目已被使用   成功预处理了大部分内容   GNU C库的源代码。如   版本1.2.5,它也可以   预处理Apple Objective C   图书馆。

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


2
2017-11-28 21:40



我不确定这是否适合我的需要。我的代码是用Java编写的。也许你建议我获取他们的资源并使用它们来预处理我的代码? - jutky


如果你真的需要条件编译而你使用 蚂蚁,您可以过滤代码并在其中进行搜索和替换。

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

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

如果你使用 Maven的,有一个类似的功能描述 这里


2
2017-07-13 11:01





使用Factory Pattern在类的实现之间切换?

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

严格来说,您并不需要预处理器来实现您想要实现的目标。最有可能满足您的要求的其他方式比我提议的那样。


1
2017-11-28 21:41



这些变化非常小。就像测试某些条件一样,提前知道所请求的结果而不是重新计算它。所以调用函数的开销可能不适合我。 - jutky


final static int appFlags = context.getApplicationInfo().flags;
final static boolean isDebug = (appFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0

0
2017-09-21 12:45