Подтвердить что ты не робот

Является ли 1/0 законным выражением Java?

Следующие компилируются в моем 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 об этом?
  • Если это законно, есть ли веская причина для этого?
    • Какая польза от этого может случиться?
4b9b3361

Ответ 1

Является ли 1/0 фактическим юридическим выражением Java, которое должно компилироваться в любое время в любом месте?

Да.

Что говорит JLS об этом?

Ничего конкретного... кроме того, что деление на ноль приведет к исключению во время выполнения. Тем не менее, JLS подтверждает возможность исключений во время выполнения в следующем определении:

"Постоянное выражение времени компиляции представляет собой выражение, обозначающее значение примитивного типа или String , которое не завершается внезапно и составлено с использованием только следующего:..."

(добавлен акцент). Таким образом, следующее не будет компилироваться:

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

Если это законно, есть ли веская причина для этого?

Хороший вопрос. Я полагаю, что это способ бросить ArithmeticException, хотя это вряд ли правдоподобная причина. Более вероятная причина для указания Java таким образом заключается в том, чтобы избежать излишней сложности в JLS и компиляторах, чтобы иметь дело с краевым случаем, который редко собирается укусить людей.

Но это все мимо. Дело в том, что 1/0 является допустимым кодом Java, и никакой компилятор Java никогда не должен указывать это как ошибку компиляции. (Было бы разумным, чтобы компилятор Java выдавал предупреждение, при условии, что для его отключения был отключен компилятор.)

Ответ 2

Я немного перебрался в базу данных ошибок и обнаружил интересную информацию.

Идентификатор ошибки 4178182: JLS не определяет поведение для 1/0 как константное выражение

Следующий код является незаконным:

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

Значение этой константы времени компиляции undefined, , поэтому это должен быть ошибкой времени компиляции. Гай Стил подтвердил около 18 месяцев что это действительно было предназначено.

Постоянная времени компиляции должна иметь статичное значение (это что делает его постоянной времени компиляции;-) Например, значение других константы, значения которых определяются константой, содержащей деление нолем являются undefined. Это влияет на семантику операторов switchопределенная привязка и непривязка и т.д.

Идентификатор ошибки 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

Идентификатор ошибки 4154563: javac принимает деление на нулевые константные выражения в выражениях case.

Сбой компилятора Java при попытке скомпилировать следующий тест. Этот тест также приводит к сбоям всех версий компилятора 1.2beta4, но ошибка отсутствует в 12.beta3. Ниже приведен пример и диагностика компилятора:

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

Оценка: Компилятор, используемый для сообщения о всех попытках разделить на постоянный нуль как ошибки времени компиляции. Это было исправлено в beta3, чтобы код был сгенерирован для деления на постоянный нуль. К сожалению, эта ошибка была введена. Компилятор должен обрабатывать деление на ноль в выражении case изящно.

Заключение

Таким образом, вопрос о компиляции или нет 1/0 должен быть оспариваемой темой обсуждения, причем некоторые люди цитируют Гая Стил, утверждающего, что это должна быть ошибка времени компиляции, а другие говорят, что это не должно. Похоже, что в конечном итоге он решил, что это не ошибка времени компиляции, а константа времени компиляции.

Ответ 4

Хорошо, если вы посмотрите на 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, за исключением float вместо double. В принципе, 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 разрешает "бесконечность" и обрабатывается иначе, чем стандартные пары (или плавает, или так далее и т.д.).

Я должен отметить, что я не имею ни малейшего понятия, как и почему это было реализовано таким образом. Когда я вижу вышеупомянутый вывод, все это кажется мне черной магией.

Ответ 5

Это законно, потому что нет, где указано, что компилятор должен сбрасывать постоянные выражения во время компиляции.

Компилятор "умный" может скомпилировать:

a = 1 + 2

как

a = 3

Но нет ничего, что говорит, что компилятор должен это сделать. Кроме этого, 1/0 является юридическим выражением, как:

int a;
int b;

a = a/b;

является юридическим выражением.

В RUNTIME он генерирует исключение, но это ошибка времени выполнения по причине.

Ответ 6

Это законно в компиляционной точке зрения, но он будет генерировать исключение, если выполняется!

причина... хорошо программирование должно допускать гибкость, поэтому все выражения и каждый отдельный код, который вы вводите, являются переменной для компилятора, поэтому в математическом выражении X/ Y компилятор не заботится если значение переменной Y (Y == 0) или любое другое число для компилятора, это переменная... если компилятор должен будет также искать значения, которые будут считаться временем выполнения, не так ли.

Ответ 7

Зачем беспокоиться об этом во время компиляции, когда вам понадобится вариант во время выполнения?

Например, если вы загружали и разбирали "0" из текстового файла, а затем пытались разделить его, Java не представляла, что вы делаете во время компиляции, потому что оно не знает содержимого этого внешний файл.

Также, если вы должны были установить любую переменную в 0 и делить на переменную, Java должна была бы отслеживать каждое возможное значение каждой переменной в каждой точке script, чтобы поймать деление на 0 при компиляции время.

Можно также сохранить согласованность и сделать исключение только для выполнения.

Ответ 8

Так как другие уже ответили на законность 1/0, перейдите к второму вопросу:

  • Если это законно, есть ли веская причина для этого?
    • Какая польза от этого может случиться?

Ответ может быть:

Чтобы поддразнивать своего коллегу.; o)

Когда коллега покидает комнату с оставленным разблокированным компьютером, пробирается и зарывается 1/0 где-то глубоко в статический инициализатор некоторого класса, который используется на ранней стадии приложения. Таким образом, он скоро узнает (или даже во время) развертывание приложения, столкнувшись с необычным ArithmeticException, и он, вероятно, поцарапает его голову на некоторое время. Используя этот отказобезопасный способ, вы можете обеспечить его относительно безобидную шутку.

P.S.: Это сработало.; О)