Является ли 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, приведенный выше fragment генерирует следующий байт-код ( javap -c Div0
)
- Что более эффективно: if (null == variable) или if (variable == null)?
- Получение шрифтов, размеров, жирного шрифта, ... и т. Д.
- Java 7 String - сложность подстроки
- Суммировать все элементы java arraylist
- Побитовое умножение и добавление в Java
Compiled from "Div0.java" public class Div0 extends java.lang.Object{ public Div0(); Code: 0: aload_0 1: invokespecial #8; //Method java/lang/Object."":()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 об этом?
- Если это законно, есть ли веская причина для этого?
- Какая польза от этого может случиться?
- В чем преимущество polymorphismа с использованием интерфейса Collection для создания объекта ArrayList?
- массив java Arrays.sort 2d
- как добиться сцепления метода в java?
- Преимущества и недостатки цепочки методов и возможность заменить все возвращаемые параметры void самим объектом
- Можете ли вы разделить stream на два streamа?
- сортировка 2D-массива String в java
- JSF 2.0 устанавливает локаль на протяжении всего сеанса из браузера и программно
- Как обновить данные в JTable Я использую TableModel
Действительно ли
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 выдавал предупреждение при условии, что для его отключения был отключен компилятор).
Я немного поработал в базе данных ошибок и обнаружил интересную информацию.
Идентификатор ошибки 4178182: JLS не определяет поведение для 1/0 как константное выражение
Следующий код является незаконным:
class X { static final int i = 1 / 0; }
Значение этой константы времени компиляции не определено, поэтому это должна быть ошибка времени компиляции. Гай Стил подтвердил около 18 месяцев назад, что это было действительно предполагаемое поведение.
Константа времени компиляции должна иметь статичное значение (это то, что делает ее постоянной времени компиляции 😉 Например, значение других констант, значения которых определяются константой, содержащей деление на ноль, не определено. Это влияет на семантику операторов
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
является оспариваемой темой обсуждения, причем некоторые люди цитируют Гая Стил, утверждающего, что это должна быть ошибка времени компиляции, а другие говорят, что это не должно. Похоже, что в конечном итоге было решено, что это не ошибка времени компиляции, а константа времени компиляции.
Java явно требует целочисленного деления на ноль, чтобы вызвать ArithmeticException
. Назначение j
не может быть отменено, потому что это нарушит спецификацию.
Ну, если вы посмотрите на Double Class, вы увидите следующее:
/** * A constant holding the positive infinity of type *
double
. It is equal to the value returned by *Double.longBitsToDouble(0x7ff0000000000000L)
. */ public static final double POSITIVE_INFINITY = 1.0 / 0.0;
Тот же расчет выполняется в classе 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 разрешает «бесконечность» и обрабатывается иначе, чем стандартные пары (или плавает, или так далее и т. Д.).
Я должен отметить, что я не имею ни малейшего понятия, как и почему это было реализовано таким образом. Когда я вижу вышеупомянутый вывод, все это кажется мне черной магией.
Это законно, потому что нет, где указано, что компилятор должен сбрасывать постоянные выражения во время компиляции.
«Умный» компилятор может скомпилировать:
a = 1 + 2
в виде
a = 3
Но нет ничего, что говорит, что компилятор должен это сделать. Помимо этого, 1/0 является юридическим выражением, как:
int a; int b; a = a/b;
является юридическим выражением.
В RUNTIME он генерирует исключение, но это ошибка времени выполнения по какой-либо причине.
Зачем беспокоиться об этом во время компиляции, когда вам все-таки понадобится вариант времени выполнения?
Например, если вы загружали и разбирали «0» из текстового файла, а затем пытались разделить его, Java не представляла, что вы делаете во время компиляции, потому что не знает содержимого этого внешнего файла.
Также, если вы должны были установить любую переменную в 0 и делить на переменную, Java должна была бы отслеживать каждое возможное значение каждой переменной в каждой точке скрипта, чтобы поймать деление на 0 во время компиляции.
Мог бы также сохранить согласованность и сделать исключение только для выполнения.
Это является законным с точки зрения компиляции , но оно будет выдавать исключение, если оно выполняется!
причина … хорошо программирование должно обеспечить гибкость, поэтому все выражения и каждый отдельный код, который вы вводите, являются переменной для компилятора, поэтому в математическом выражении X / Y компилятору все равно, если значение переменной Y ( Y == 0 ) или любое другое число для компилятора – это переменная … если компилятор должен будет также искать значения, которые будут считаться временем выполнения, не так ли.
Поскольку другие уже ответили на законность 1/0
, перейдем ко второму вопросу:
- Если это законно, есть ли веская причина для этого?
- Какая польза от этого может случиться?
Ответ может быть следующим:
Дразнить своего коллегу. ; О)
Когда коллега покидает комнату с оставленным разблокированным компьютером, пробирается и прорывается
1/0
где-то глубоко в статический инициализатор некоторого classа, который используется на ранней стадии приложения. Таким образом, он узнает достаточно скоро после (или даже во время) развертывания приложения, столкнувшись с необычнымArithmeticException
и он, вероятно, поцарапает его голову на некоторое время. Используя этот быстрый способ, вы можете гарантировать, что это относительно безобидная шутка.
PS: Это сработало. ; О)