41

以下は私の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はさらに奇妙な動作をし、サイレントにコンパイルしますが、割り当てをバイトコードiから完全に削除しkます (おそらく、それらがどこにも使用されていないと判断したためです)。1/0

質問は次のとおりです。

  • 1/0実際、いつでもどこでもコンパイルできる正当な Java 式ですか ?
    • JLSはそれについて何と言っていますか?
  • これが合法である場合、それには正当な理由がありますか?
    • これは何の役に立つのでしょうか?
4

8 に答える 8

34

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 コンパイラーが警告を発行するのは、それをオフにするコンパイラー・スイッチがあれば、合理的です。)

于 2010-05-29T06:28:35.160 に答える
20

Bug Database を調べてみたところ、興味深い情報がいくつか見つかりました。

バグ ID 4178182: JLS は 1/0 の動作を定数式として指定していません

次のコードは違法です。

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

バグ ID 4154563: javac は、case 式でゼロ除算の定数式を受け入れます。

次のテストをコンパイルしようとすると、Java コンパイラがクラッシュします。このテストでは、すべての 1.2beta4 コンパイラ バージョンもクラッシュしますが、12.beta3 にはバグがありません。例とコンパイラの診断は次のとおりです。

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

評価:コンパイラは、定数ゼロで割ろうとするすべての試みをコンパイル時エラーとして報告していました。これは beta3 で修正され、定数ゼロによる除算のコードが生成されるようになりました。残念ながら、このバグが導入されました。コンパイラは、ケース式のゼロ除算を適切に処理する必要があります。

結論

そのため、コンパイルすべきかどうかという問題は1/0議論の争点となり、Guy Steele を引用して、これはコンパイル時のエラーであるべきだと主張する人もいれば、そうすべきではないと言う人もいます。最終的には、コンパイル時エラーでもコンパイル時定数でもないと判断されたようです。

于 2010-05-29T13:28:52.073 に答える
3

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;

double の代わりに float を使用することを除いて、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。double と見なされますが、文字列を出力します。

質問に答えると、はい、Java では合法ですが、1/0 は「無限大」に解決され、標準の Double (または float など) とは異なる方法で処理されます。

どのように、またはなぜこのように実装されたのか、私にはまったく手がかりがないことに注意してください。上記の出力を見ると、すべて黒魔術のように思えます。

于 2010-05-29T10:18:17.663 に答える
2

Javaでは、 をトリガーするためにゼロによる整数除算が明示的に必要ArithmeticExceptionです。j仕様に違反するため、への代入は省略できません。

于 2010-05-29T06:30:51.287 に答える
0

の合法性については他の人がすでに回答しているので1/0、2 番目の質問に移りましょう。

  • これが合法である場合、それには正当な理由がありますか?
    • これは何の役に立つのでしょうか?

答えは次のとおりです。

あなたの同僚をからかうために。;o)

同僚がコンピューターのロックを解除したまま部屋を出ると、アプリケーションの初期段階で使用されるクラスの静的初期化子1/0の奥深くに忍び込みます。このようにして、彼はアプリケーションの展開後 (または展開中にさえ) 異常に遭遇することですぐに気付くことができ、おそらくしばらく頭を悩ませるでしょう。このフェイルファストの方法を使用すると、比較的無害なジョークであることを確認できます。ArithmeticException

PS: うまくいきました。;o)

于 2016-01-08T09:38:48.493 に答える
0

コンパイラがコンパイル時に定数式を折りたたむことになっていることはどこにもないため、これは合法です。

「スマート」コンパイラは次のようにコンパイルできます。

a = 1 + 2

なので

a = 3

しかし、コンパイラがそうしなければならないということは何もありません。それ以外では、1/0 は次のような正当な式です。

int a;
int b;

a = a/b;

は法的な表現です。

RUNTIME で例外がスローされますが、これは何らかの理由でランタイム エラーです。

于 2010-05-29T06:27:38.547 に答える
0

コンパイルの観点では合法ですが、実行すると例外がスローされます。

その理由は...まあ、プログラミングは柔軟性を持たせる必要があるため、入力するすべての式とすべてのコードはコンパイラの変数です。したがって、数式 X/ Yでは、 Y 変数の値が ( Y== 0 ) またはコンパイラのその他の数値これは変数です...コンパイラが値も確認する必要がある場合、それは実行時と見なされます。

于 2010-05-29T06:39:37.603 に答える
0

とにかく実行時のバリアントが必要になるのに、なぜコンパイル時にこれをキャッチする必要があるのでしょうか?

たとえば、テキスト ファイルから "0" を読み込んで解析し、それで除算しようとした場合、Java は外部ファイルの内容を認識していないため、コンパイル時に何をしていたのかわかりません。

また、任意の変数を 0 に設定してその変数で除算する場合、Java はコンパイル時に 0 による除算を検出するために、スクリプト内のすべてのポイントですべての変数のすべての可能な値を追跡する必要があります。

物事の一貫性を保ち、ランタイムのみの例外にすることもできます。

于 2010-05-29T06:42:14.643 に答える