代码之家  ›  专栏  ›  技术社区  ›  polygenelubricants

1/0是合法的Java表达式吗?

  •  37
  • polygenelubricants  · 技术社区  · 15 年前

    以下内容在我的Eclipse中很好地编译:

    final int j = 1/0;
    // compiles fine!!!
    // throws ArithmeticException: / by zero at run-time
    

    Java首先防止许多“哑代码”甚至编译。 "Five" instanceof Number 不编译!)因此,这甚至没有产生那么多的警告,这对我来说是非常令人惊讶的。当您考虑到常量表达式在编译时可以进行优化这一事实时,这种兴趣会加深:

    public class Div0 {
        public static void main(String[] args) {
            final int i = 2+3;
            final int j = 1/0;
            final int k = 9/2;
        }
    }
    

    在Eclipse中编译,上面的代码片段生成以下字节码( javap -c Div0 )

    Compiled from "Div0.java"
    public class Div0 extends java.lang.Object{
    public Div0();
      Code:
       0:   aload_0
       1:   invokespecial   #8; //Method java/lang/Object."<init>":()V
       4:   return
    
    public static void main(java.lang.String[]);
      Code:
       0:   iconst_5
       1:   istore_1      // "i = 5;"
       2:   iconst_1
       3:   iconst_0
       4:   idiv
       5:   istore_2      // "j = 1/0;"
       6:   iconst_4
       7:   istore_3      // "k = 4;"
       8:   return
    
    }
    

    如你所见, i k 将分配优化为编译时间常量,但 0 (必须在编译时检测到)只是按原样编译。

    javac 1.6.0_17 更奇怪的是,它会安静地编译,但会将任务转移到 K 完全脱离了字节码(可能是因为它决定了它们不在任何地方使用),但是离开了 1/0 完好无损(因为删除它会导致完全不同的程序语义)。

    所以问题是:

    • 1/0 实际上,任何时候都应该编译的法律Java表达式?
      • 捷豹路虎对此有何看法?
    • 如果这是合法的,有充分的理由吗?
      • 这可能有什么好处?
    8 回复  |  直到 9 年前
        1
  •  32
  •   Stephen C    13 年前

    1/0 实际上,任何时候都应该编译的法律Java表达式?

    对。

    捷豹路虎对此有何看法?

    没什么特别的…除了说被零除将导致运行时异常之外。但是,JLS承认以下定义中存在运行时异常的可能性:

    “编译时常量表达式是表示基元类型或字符串值的表达式。” 不会突然完成的 和仅使用以下内容组成:

    (增加了重点。)因此以下内容无法编译:

    switch(i) {
        case 1:
        case 1 + 1: 
        case 1 / 0:  // compilation error.
    }
    

    如果这是合法的,有充分的理由吗?

    好问题。我想这是一种投掷 ArithmeticException 尽管这不是一个合理的理由。以这种方式指定Java的一个更可能的原因是避免JLS和编译器中不必要的复杂性来处理边缘情况,这种情况很少会对人产生影响。

    但这一切都在旁边。事实是 1/0 是一个有效的Java代码,没有Java编译器会将它标记为编译错误。(Java编译器发出警告是合理的,只要有一个编译器开关关闭它)。

        2
  •  20
  •   polygenelubricants    15 年前

    我对bug数据库进行了一些挖掘,发现了一些有趣的信息。

    Bug ID 4178182: JLS doesnt specify behavior for 1/0 as a constant expression

    以下代码是非法的:

    class X { static final int i = 1 / 0; }
    

    编译时常量的值未定义, 因此这 必须是编译时错误。 盖伊·斯蒂尔确认了大约18个月 以前,这确实是有意的行为。

    编译时常量的值必须静态可用(即 什么使它成为编译时常量;-)例如,其他 其值由包含除法的常量确定的常量 未定义by零。这会影响 switch 声明, 明确分配、不分配等。

    Bug ID 4089107: javac treats integer division by (constant) zero as an error

    public class zero {
       public static void main(String[] args) {
          System.out.println(1/0);
       }
    }
    

    运行上述结果:

    zero.java:3: Arithmetic exception.
         System.out.println(1/0);
                             ^
    1 error
    

    Bug ID 4154563: javac accepts division by zero constant expressions in case expressions.

    Java编译器在编译下一个测试时崩溃。此测试也会使所有1.2beta4编译器版本崩溃,但12.beta3中没有bug。示例和编译器诊断如下:

    public class B {
       public static void main(String argv[]) {
          switch(0){
             case 0/0:
          }
      }
    }
    

    评价: 编译器用来报告所有试图除以常数零的尝试,作为编译时错误。这是固定的 在beta3中,这样代码将被生成,并被常数零除。不幸的是,这个bug被引入了。编译器应该在case表达式中优雅地处理被零除。

    结论

    所以问题是 1/0 “应该编译”是一个有争议的讨论话题,有些人引用盖伊·斯蒂尔的话说,这应该是一个编译时错误,而另一些人则认为不应该。看来最终决定它既不是编译时错误,也不是编译时常量。

        3
  •  2
  •   Marcelo Cantos    15 年前

    爪哇 explicitly requires 整数除以零触发 ArithmeticException . 分配给 j 无法删除,因为这将违反规范。

        4
  •  2
  •   Corey    15 年前

    好吧,如果你看一下双班,你会看到以下内容:

    /**
     * A constant holding the positive infinity of type
     * <code>double</code>. It is equal to the value returned by
     * <code>Double.longBitsToDouble(0x7ff0000000000000L)</code>.
     */
    public static final double POSITIVE_INFINITY = 1.0 / 0.0;
    

    在float类中进行相同的计算,除了使用float而不是double。基本上,1/0返回一个非常大的数字,大于double.max_值。

    以下代码:

    public static void main(String[] args) {
        System.out.println(Double.POSITIVE_INFINITY);
        System.out.println(Double.POSITIVE_INFINITY > Double.MAX_VALUE);
    }
    

    输出:

    Infinity
    true
    

    注意打印时的特殊情况 Double.POSITIVE_INFINITY . 它会打印出一根绳子,尽管它被认为是双份的。

    为了回答这个问题,是的,它在Java中是合法的,但是1/0解决了“无穷大”,并且不同于标准的双倍(或浮动,等等)。

    我应该注意到,我不知道它是如何或为什么以这种方式实现的。当我看到上面的输出时,我觉得这一切都像是黑色魔法。

        5
  •  0
  •   Will Hartung    15 年前

    这是合法的,因为编译器不应该在编译时折叠常量表达式。

    “智能”编译器可能编译:

    a = 1 + 2
    

    作为

    a = 3
    

    但是没有什么能说明编译器必须这样做。除此之外,1/0是一种法律表达,就像:

    int a;
    int b;
    
    a = a/b;
    

    是一种法律表达。

    在运行时,它抛出一个异常,但这是一个运行时错误,原因是。

        6
  •  0
  •   Daniel    15 年前

    当您无论如何都需要一个运行时变量时,为什么还要在编译时捕获它呢?

    例如,如果你正在从文本文件中加载和解析“0”,然后尝试按它划分,Java就不知道你在编译时做了什么,因为它不知道那个外部文件的内容。

    此外,如果要将变量设置为0并将其除以变量,则Java必须跟踪脚本中的每个点的每个变量的每一个可能值,以便在编译时捕获0的除法。

    最好保持事物的一致性,并使其成为仅运行时的异常。

        7
  •  0
  •   TacB0sS    15 年前

    这是合法的 编译观点 ,但如果执行它将引发异常!

    原因…好的编程必须允许灵活性,因此您键入的所有表达式和每个代码都是编译器的变量,因此在数学表达式x中/ Y 编译器不关心y变量值是否为( y=0 )或者编译器的其他数字,这是一个变量…如果编译器也必须查看值,那将被视为运行时,不是吗?

        8
  •  0
  •   charlie    9 年前

    因为其他人已经回答了 1/0 ,让我们进入第二个问题:

    • 如果这是合法的,有充分的理由吗?
      • 这有什么用呢?

    答案可能是:

    嘲笑你的同事。 o)

    当这位同事离开房间时,他的电脑没上锁,就溜过去埋了。 1/0 在某个很深的地方 静态初始化器 在应用程序早期使用的某些类。这样,在部署应用程序之后(甚至在部署期间),他会很快发现异常情况。 ArithmeticException 他可能会抓头一会儿。使用这种快速失败的方法,你可以确保这是一个相对无害的笑话。

    P.S.:它起作用了。o)