【问题标题】:How does the Compiler treat Selection Statement Variations编译器如何处理选择语句变体
【发布时间】:2014-08-05 19:16:45
【问题描述】:

以下所有三种方法在功能上都是相同的 - 但是它们都有我认为的风格差异。当这些被编译时,编译器会以不同的方式对待它们吗? (我缺乏检查和理解反编译字节码的知识)

// Method 1
private boolean isAcceptableRange(final int a, final int b) {       
    if ((Math.abs(a - b)) <= range) {
        return true;
    } else {
        return false;
    }
}

// Method 2
private boolean isAcceptableRange(final int a, final int b) {       
    if ((Math.abs(a - b)) <= range) {
        return true;
    }
    return false;
}

// Method 3
private boolean isAcceptableRange(final int a, final int b) {
    if ((Math.abs(a - b)) <= range)
        return true;
    return false;
}

我最初的想法是 #1 将是独一无二的,因为额外的 else 子句,其中 #2 和 #3 最终将是相同的。这意味着在优化磁盘/ROM 空间效率和可能的指令效率(考虑嵌入式)时,请使用选项 #2 或 #3。

还是编译器知道最终结果并“优化”掉它?

【问题讨论】:

  • 您可以使用javap -c查看字节码。
  • 我仍然建议使用return (Math.abs(a - b)) &lt;= range; 而不是您的三个变体中的任何一个。

标签: java compiler-construction jvm javac bytecode


【解决方案1】:

还是编译器知道最终结果并“优化”掉它?

是的,确实如此。让我们看看生成的字节码:

javap -c -private MyClass.class
  • javap: 是一个工具,它(除其他外)让您可以查看 java 类的字节码,它随 JDK 一起提供。
  • -c:将代码反汇编成字节码
  • -private:显示你的类中的私有成员和方法。
  • MyClass.class: 编译好的类

private boolean isAcceptableRange1(int, int);
    Code:
       0: iload_1       
       1: iload_2       
       2: isub          
       3: invokestatic  #1                  // Method java/lang/Math.abs:(I)I
       6: aload_0       
       7: getfield      #2                  // Field range:I
      10: if_icmpgt     15
      13: iconst_1      
      14: ireturn       
      15: iconst_0      
      16: ireturn       

  private boolean isAcceptableRange2(int, int);
    Code:
       0: iload_1       
       1: iload_2       
       2: isub          
       3: invokestatic  #1                  // Method java/lang/Math.abs:(I)I
       6: aload_0       
       7: getfield      #2                  // Field range:I
      10: if_icmpgt     15
      13: iconst_1      
      14: ireturn       
      15: iconst_0      
      16: ireturn       

  private boolean isAcceptableRange3(int, int);
    Code:
       0: iload_1       
       1: iload_2       
       2: isub          
       3: invokestatic  #1                  // Method java/lang/Math.abs:(I)I
       6: aload_0       
       7: getfield      #2                  // Field range:I
      10: if_icmpgt     15
      13: iconst_1      
      14: ireturn       
      15: iconst_0      
      16: ireturn  

我调用了方法isAcceptableRange1isAcceptableRange2isAcceptableRange3。如您所见,所有方法都有完全相同的字节码。

【讨论】:

    【解决方案2】:

    我也没有看过字节码。但是根据我对编译器技术的一般知识:if (condition) {then-part} else {else-part} 导致的代码基本上看起来像:

    if (!condition) branch to Label_1;
    perform then-part;
    branch to Label_2;
    Label_1:
    perform else-part;
    Label_2:
    

    在您的示例 #1 中,“then-part”是 return 语句。 return 语句的代码要么是 return 指令,要么是 branch 到子例程代码末尾的某个点,它执行清理(包括任何有效的 finally)然后返回.一个体面的编译器不会在另一个无条件分支或return 指令之后生成分支(或任何其他代码),因为该代码永远不会被执行。所以示例 #1 最终看起来像

    if (!condition) branch to Label_1;
    set return value to "true";
    return or branch to cleanup code;
    Label_1:
    set return value to "false";
    return or branch to cleanup code;
    

    这将与示例 #2 或 #3 生成的代码完全相同。

    所以你的问题的答案是:不,编译器不会区别对待你的任何示例,除非它是一个非常糟糕的编译器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-26
      相关资源
      最近更新 更多