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

2022-09-01 02:35:34

以下内容在我的 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

}

如您所见,和 赋值被优化为编译时常量,但除法(必须在编译时可检测到)只是按原样编译。ik0

javac 1.6.0_17表现得更奇怪,静默编译,但将分配完全从字节码中删除(可能是因为它确定它们不在任何地方使用),但保留完整的(因为删除它会导致完全不同的程序语义)。ik1/0

所以问题是:

  • 实际上是一个合法的Java表达式,应该随时随地编译吗?1/0
    • JLS对此有何评论?
  • 如果这是合法的,那么有充分的理由吗?
    • 这可能有什么好处呢?

答案 1

实际上是一个合法的Java表达式,应该随时随地编译吗?1/0

是的。

JLS对此有何评论?

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

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

(着重号是后加的。因此,以下内容将无法编译:

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

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

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

但这一切都是顺便的。事实是,这是有效的Java代码,任何Java编译器都不应该将其标记为编译错误。(Java 编译器发出警告是合理的,前提是有一个编译器开关可以将其关闭。1/0


答案 2

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

bug ID 4178182:JLS 未将 1/0 的行为指定为常量表达式

以下代码是非法的:

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

此编译时常量的值未定义,因此这必须是编译时错误。盖伊·斯蒂尔(Guy Steele)在大约18个月前证实,这确实是预期的行为。

编译时常量必须具有静态可用的值(这就是使其成为编译时常量的原因;-)例如,其值由包含除以零的常量确定的其他常量的值是未定义的。这会影响语句的语义,确定的分配和取消分配等。switch

bug ID 4089107:javac 将整数除以(常量)零视为错误

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 在大小写表达式中接受除以零常量表达式。

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

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

评估:编译器用于将所有尝试除以常量零的尝试报告为编译时错误。这在beta3中已修复,因此将生成代码以除以常量零。不幸的是,这个错误被引入。编译器应优雅地处理大小写表达式中的除以零。

结论

因此,是否应该编译的问题是一个有争议的讨论话题,有些人引用Guy Steele的话说这应该是一个编译时错误,而另一些人则说它不应该。似乎最终决定它既不是编译时错误也不是编译时常量。1/0


推荐