1/0是合法的Javaexpression式吗?

下面的代码在我的Eclipse中编译得很好:

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

Java首先防止了许多“哑代码”的编译(例如"Five" instanceof Number不能编译!),所以事实上这并没有产生太多的警告,这让我非常惊讶。 如果考虑到在编译时允许对常量expression式进行优化的事实,阴谋就会加深:

 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 } 

正如你所看到的, ik赋值是作为编译时常量进行优化的,但是除以0 (在编译时必须能够被检测到)被简单编译。

javac 1.6.0_17行为更奇怪,静静地编译,但完全从字节码中删除了ik的赋值(可能是因为它确定它们没有在任何地方使用),但保持1/0完整(因为删除它将导致一个完全不同的程序语义)。

所以问题是:

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

实际上1/0是合法的Javaexpression式,应该随时随地编译?

是。

JLS对此有何评论?

没有什么具体的…除了说除以零将导致运行时exception。 但是,JLS承认在以下定义中存在运行时exception的可能性:

“编译时常量expression式是一个expression式,表示一个原始types的值或者一个不会突然完成的string,并且只用下面的方式组成:…”

(强调添加。)所以下面不会编译:

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

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

好问题。 我想这是一种抛出ArithmeticException的方法,尽pipe这不是一个合理的理由。 以这种方式指定Java的一个更可能的原因是为了避免JLS和编译器中的不必要的复杂性,以处理一个极less遇到人的边缘案例。

但是这一切都是靠的。 事实是1/0是有效的Java代码,Java编译器不应该将其标记为编译错误。 (如果有编译器开关将其closures,Java编译器会发出警告是合理的。)

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

错误ID 4178182:JLS不会将1/0的行为指定为常量expression式

以下代码是非法的:

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

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

编译时常量的值必须是静态的(这就是编译时常量;-)例如,其值是由包含除零的常量决定的其他常量的值是未定义的。 这影响了switch语句的语义,明确的分配和取消分配等。

错误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在caseexpression式中接受用零常量expression式除法。

Java编译器在尝试编译下一个testing时崩溃。 这个testing也崩溃了所有的1.2beta4编译器版本,但bug在12.beta3中不存在。 一个示例和编译器诊断如下:

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

评估: 编译器用于报告所有尝试除以常量零作为编译时错误。 这是固定在beta3,以便代码将生成除以常量零。 不幸的是,这个bug被引入。 编译器应该优雅地处理caseexpression式中的零除。

结论

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

Java 显式地要求零除以触发ArithmeticException 。 对j的任务不能被删除,因为这将违反规范。

那么,如果你看看Double类,你会看到以下内容:

 /** * 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类中计算相同,除了浮点数而不是双精度数。 基本上,1/0会返回一个非常大的数字,比Double.MAX_VALUE大。

以下代码:

 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的特殊情况。 它打印出一个string,虽然它被认为是一个双。

为了回答这个问题,是的,它在Java中是合法的,但是1/0parsing为“无穷大”,并且与标准双精度(或浮点数,等等等等)的处理方式不同。

我应该注意到,我没有丝毫的线索如何或为什么这样实施。 当我看到上面的输出时,对我来说这一切似乎都是黑魔法。

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

一个“智能”编译器可能会编译:

 a = 1 + 2 

 a = 3 

但没有什么说编译器必须这样做。 除此之外,1/0是一个合法的expression式,就像:

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

是一个法律expression。

在RUNTIME它引发一个exception,但这是一个运行时错误的原因。

为什么还要在编译时捕捉这个问题呢?无论如何,当你需要一个运行时variables的时候呢?

例如,如果你正在从一个文本文件中加载和parsing“0”,然后尝试除以它,Java将不知道你在编译时正在做什么,因为它不知道这个外部文件的内容。

另外,如果要将任何variables设置为0并除以variables,则Java将必须跟踪脚本中每个点处的每个variables的每个可能值,以便在编译时间除以0。

不妨将事情保持一致,并使其成为运行时唯一的例外。

编译的观点上是合法 ,但是如果执行的话会抛出exception!

理由是编程必须允许灵活性,因此所有的expression式和你input的每一个代码都是编译器的variables,因此在mathexpression式X / Y中 ,编译器不关心Yvariables值是( Y == 0 )或编译器的任何其他数字,这是一个variables…如果编译器也必须查看值,这将被视为运行时,不是。

由于其他人已经回答了1/0的合法性,让我们来看第二个问题:

  • 如果这是合法的,是否有充分的理由呢?
    • 这可能有什么好处?

答案可能是:

挑逗你的同事。 ; O)

当同事离开房间时,他的电脑仍然处于解锁状态,偷偷摸摸地钻进了某处,并在某处深入到某个在应用程序中早期使用的类的静态初始化程序中。 这样一来,在应用程序部署之后(甚至在执行期间),他会很快发现遇到exception的ArithmeticException ,他可能会暂时忘记他的头。 使用这种快速的方法,你可以确保这是一个相对无害的笑话。

PS:它的工作。 ; O)