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

发布于 2024-09-03 02:16:56 字数 1458 浏览 5 评论 0原文

下面的代码在我的 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

}

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

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

所以问题是:

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

The following compiles fine in my Eclipse:

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

Java prevents many "dumb code" from even compiling in the first place (e.g. "Five" instanceof Number doesn't compile!), so the fact this didn't even generate as much as a warning was very surprising to me. The intrigue deepens when you consider the fact that constant expressions are allowed to be optimized at compile time:

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

Compiled in Eclipse, the above snippet generates the following bytecode (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

}

As you can see, the i and k assignments are optimized as compile-time constants, but the division by 0 (which must've been detectable at compile-time) is simply compiled as is.

javac 1.6.0_17 behaves even more strangely, compiling silently but excising the assignments to i and k completely out of the bytecode (probably because it determined that they're not used anywhere) but leaving the 1/0 intact (since removing it would cause an entirely different program semantics).

So the questions are:

  • Is 1/0 actually a legal Java expression that should compile anytime anywhere?
    • What does JLS say about it?
  • If this is legal, is there a good reason for it?
    • What good could this possibly serve?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

酒几许 2024-09-10 02:16:56

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

是的。

JLS 对此有何评论?

没有什么具体的...除了说除以零会导致运行时异常。然而,JLS 在以下定义中承认运行时异常的可能性:

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

(添加了强调。)因此以下内容不会编译:

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

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

好问题。我认为这是抛出 ArithmeticException 的一种方法,尽管这不是一个合理的理由。以这种方式指定 Java 的一个更可能的原因是避免 JLS 和编译器中不必要的复杂性来处理很少会困扰人们的边缘情况。

但这都不是重点。事实是 1/0 >> 是 <<有效的 Java 代码,任何 Java 编译器都不应该将其标记为编译错误。

(如果还有一种方法可以关闭警告,那么 Java 编译器发出警告是合理的。但另一方面,除以常量零的情况非常罕见,因此实现这一点的价值值得怀疑。)

Is 1/0 actually a legal Java expression that should compile anytime anywhere?

Yes.

What does JLS say about it?

Nothing specific ... apart from saying that division by zero will result in a runtime exception. However, the JLS acknowledges that possibility of runtime exceptions in the following definition:

"A compile-time constant expression is an expression denoting a value of primitive type or a String that does not complete abruptly and is composed using only the following: ..."

(Emphasis added.) So the following would NOT compile:

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

If this is legal, is there a good reason for it?

Good question. I suppose that it is a way to throw ArithmeticException though that is hardly a plausible reason. A more likely reason for specifying Java this way is to avoid unnecessary complexity in the JLS and compilers to deal with an edge case that is rarely going to bite people.

But this is all beside the point. The fact is that 1/0 >>is<< valid Java code, and no Java compiler should ever flag this as a compilation error.

(It would be reasonable for a Java compiler to issue a warning, provided that there was also a way to turn off the warning. But the flipside that division by a constant zero is sufficiently rare as to make the value of implementing this doubtful.)

淡写薰衣草的香 2024-09-10 02:16:56

我深入研究了错误数据库,发现了一些有趣的信息。

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

以下代码非法:

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

这个编译时常量的值是未定义的,因此
一定是编译时错误。
Guy Steele 确认大约 18 个月
之前这确实是预期的行为。

编译时常量的值必须静态可用(即
是什么使它成为编译时常量;-) 例如, other 的值
其值由包含除法的常量确定的常量
为零是未定义的。这会影响 switch 语句的语义,
明确的赋值和取消赋值等

Bug ID 4089107:javac 处理整数除以(常量)零作为一个错误

公共类零{
   public static void main(String[] args) {
      System.out.println(1/0);
   }
}

运行上面的结果:

zero.java:3:算术异常。
     System.out.println(1/0);
                         ^
1 error

错误 ID 4154563:javac 在 case 表达式中接受除以零的常量表达式。

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

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

评估:编译器用于将所有除以常量零的尝试报告为编译时错误。这个问题在 beta3 中得到了修复,以便生成除以常量零的代码。不幸的是这个错误被引入了。编译器应该优雅地处理 case 表达式中的除以零的情况。


结论

因此,1/0 是否应该编译的问题是一个有争议的讨论话题,一些人引用 Guy Steele 的说法,声称这应该是一个编译时错误,而其他人则说它不应该' t。看来最终决定它既不是编译时错误也不是编译时常量。

I did some digging into the Bug Database, and discovered some interesting information.

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

The following code is illegal:

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

The value of this compile-time constant is undefined, therefore this
has to be a compile-time error.
Guy Steele confirmed about 18 months
ago that this was indeed the intended behaviour.

A compile-time constant has to have its value available statically (that's
what makes it a compile-time constant ;-) For example, the value of other
constants whose values are determined by a constant that contains a division
by zero are undefined. This affects the semantics of switch statements,
definite assigment and unassignment, etc.

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);
   }
}

Running the above yields:

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 compiler crashes while trying to compile next test. This test also crashes all 1.2beta4 compiler versions, but bug is absent in 12.beta3. An example and compiler diagnostics follow:

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

Evaluation: The compiler used to report all attempts to divide by the constant zero as compile-time errors. This was fixed in beta3 so that code would be generated for division by constant zero. Unfortunately this bug was introduced. The compiler should handle a division by zero in a case expression gracefully.

Conclusion

So the question of whether or not 1/0 should compile was a contested topic of discussion, with some people quoting Guy Steele claiming that this should be a compile time error, and others saying that it shouldn't. It seems that ultimately it's decided that it's neither a compile-time error nor a compile-time constant.

甜尕妞 2024-09-10 02:16:56

那么,如果您查看 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 时的特殊情况。它打印出一个字符串,尽管它被视为双精度。

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

我应该指出,我完全不知道它是如何或为什么以这种方式实现的。当我看到上面的输出时,这对我来说就像黑魔法。

Well, if you look into the Double class, you will see the following:

/**
 * 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;

The same calculation is made in the Float class, except with floats instead of doubles. Basically, 1/0 returns a really, really big number, larger than Double.MAX_VALUE.

This following code:

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

Outputs:

Infinity
true

Note the special case in printing out Double.POSITIVE_INFINITY. It prints out a string, though it's regarded as a double.

To answer the question, yes it is legal in Java, but 1/0 resolves to "infinity" and is treated differently from standard Doubles (or floats, or so on and so forth).

I should note that I do not have the slightest clue how or why it was implemented this way. When I see the above output, it all seems like black magic to me.

神也荒唐 2024-09-10 02:16:56

Java 明确要求整数除以零触发 ArithmeticException。无法省略对 j 的分配,因为这会违反规范。

Java explicitly requires integer division by zero to trigger an ArithmeticException. The assignment to j can't be elided because that would violate the spec.

红焚 2024-09-10 02:16:56

这是合法的,因为没有任何地方规定编译器应该在编译时折叠常量表达式。

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

a = 1 + 2

as

a = 3

但没有任何内容表明编译器必须这样做。除此之外,1/0 是一个合法表达式,就像:

int a;
int b;

a = a/b;

是一个合法表达式一样。

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

It's legal because no where is it a given that the compiler is supposed to fold constant expressions at compile time.

A "smart" compiler might compile:

a = 1 + 2

as

a = 3

But there's nothing that says the compiler HAS to do that. Other than that, 1/0 is a legal expression just like:

int a;
int b;

a = a/b;

is a legal expression.

At RUNTIME it throws an exception, but that's a runtime error for a reason.

人间不值得 2024-09-10 02:16:56

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

例如,如果您从文本文件加载并解析“0”,然后尝试除以它,Java 将不知道您在编译时做了什么,因为它不知道该外部文件的内容。

此外,如果要将任何变量设置为 0 并除以该变量,Java 必须跟踪脚本中每个点上每个变量的每个可能值,以便在编译时捕获除以 0 的情况。

不妨保持一致并使其成为仅运行时的异常。

Why bother catching this at compile-time, when you're going to need a run-time variant anyway?

For example, if you were loading and parsing "0" from a text file, then tried to divide by it, Java would have no idea what you were doing at compile-time because it doesn't know the contents of that external file.

Also if you were to set any variable to 0 and divide by the variable, Java would have to keep track of every possible value of every variable at every point in the script in order to catch a divide by 0 at compile time.

Might as well keep things consistent and make it a runtime-only exception.

橘香 2024-09-10 02:16:56

编译的角度来看是合法的,但是如果执行的话会抛出异常!

原因...良好的编程必须允许灵活性,因此您输入的所有表达式和每个代码都是编译器的变量,因此在数学表达式 X/Y 中,编译器不关心 Y 是否变量值是 (Y==0) 或编译器的任何其他数字,这是一个变量...如果编译器还必须查看值,那将被视为运行时,不会它。

It is legal in compiling point of view, but it would throw an exception if executed!

the reason... well programming must allow flexibility therefore all the expressions and every single code you type is a variable for the compiler, thus in the mathematical expression X/Y the compiler does not care if the Y variable value is (Y==0) or any other number for the compiler this is a variable... if the compiler would have to look at values also, that would be considered runtime, wouldn't it.

じее 2024-09-10 02:16:56

既然其他人已经回答了1/0的合法性,那么让我们转向第二个问题:

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

答案可能是:

取笑你的同事。 ;o)

当同事离开房间且计算机未上锁时,偷偷溜过去并将 1/0 埋入早期使用的某个类的静态初始化程序的某个深处该应用程序。这样,他就会在应用程序部署后(甚至在部署期间)通过遇到不寻常的 ArithmeticException 很快发现问题,并且他可能会摸不着头脑一段时间。使用这种快速失败的方法,您可以确保这是一个相对无害的笑话。

PS:它起作用了。 ;o)

Since others already answered the legality of 1/0, let's move to the second question:

  • If this is legal, is there a good reason for it?
    • What good could this possibly serve?

An answer could be:

To tease a colleague of yours. ;o)

When the colleague leaves the room with his computer left unlocked, sneak by and burry 1/0 somewhere deep into a static initializer of some class that is used early in the application. This way he will find out soon enough after (or even during) the deployment of the application by encountering the unusual ArithmeticException and he will probably scratch his head for a while. Using this fail-fast way you can ensure it's a relatively harmless joke.

P.S.: It worked. ;o)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文