try-catch для деления на ноль

Мой вопрос касается блоков try-catch на примере простого деления на ноль. Вы видите первую строку try? Если я приведу любую из этих двух переменных к двойному значению, программа не распознает блок catch. На мой взгляд, независимо от того, бросаю я или нет, должен выполняться только блок catch. Что не так в этом коде?

public static void main(String[] args) {

    int pay=8,payda=0;  

    try {

        double result=pay/(double)payda; // if I cast any of the two variables, program does not recognize the catch block, why is it so?
        System.out.println(result);
        System.out.println("inside-try");

    } catch (Exception e) {

        System.out.println("division by zero exception");
        System.out.println("inside-catch");

    }
}

person Firat    schedule 13.02.2010    source источник


Ответы (7)


Деление на ноль допустимо для чисел с плавающей запятой.

  • 1/0 дает Бесконечность.
  • (-1)/0 дает -Бесконечность.
  • 0/0 дает NaN.

Эти «числа» правильно определены в IEEE 754.

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

person kennytm    schedule 13.02.2010
comment
Если деление с плавающей запятой не может генерировать исключение, то следует удалить try/catch и вместо этого использовать коды ошибок. - person Hamish Grubijan; 14.02.2010
comment
Я не думаю, что int бросает, потому что он не может представлять бесконечность. 1/0 просто бессмысленно, поэтому и выкидывает. Плавающая точка, с другой стороны, немного помахала рукой и сказала, что она должна быть бесконечной. - person GManNickG; 15.02.2010

Я бы предложил проверить:

if (divisor != 0) {
    // calculate
}

а не ловить исключение

person Bozho    schedule 13.02.2010
comment
Если делитель не является целым числом, ненулевые делители могут легко привести к непригодным результатам, например, большой номинал и маленький делитель: Double div=Double.MAX_VALUE/0,5; дает Бесконечность. Я рекомендую проверять Double.isInfinite и Double.isNaN (или эквиваленты Float) при любых рискованных вычислениях по сравнению с проверкой делителя != 0. - person Steve Zobell; 05.02.2016
comment
предполагалось, что они являются целыми числами, как в приведенном выше примере. - person Bozho; 16.02.2016

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

  • Если ожидается исключение, обычно проще, понятнее и эффективнее определить условие, которое приведет к возникновению исключения. В этом случае, если вы ожидаете, что divisor иногда будет равно нулю, лучше проверить его перед делением.

  • С другой стороны, если исключение совершенно неожиданное (т. е. «ошибка»), лучшая стратегия — позволить исключению распространиться на самый внешний уровень. В этот момент ваше приложение catch должно перехватывать все исключения, регистрировать трассировку стека исключений и выходить из системы.

  • Помимо предыдущей пули, не стоит ловить Exception, RuntimeException, Throwable или Error. Это приведет к перехвату большого количества исключений, которые вы, вероятно, не собираетесь перехватывать. В этом случае, поскольку вы ожидаете ArithmeticException, вы должны поймать именно это.

  • Редко правильно использовать float или double в финансовых приложениях. Эти типы не являются точными, но финансовые приложения обычно требуют точной обработки количества денег.

  • В более общем смысле числа с плавающей запятой по своей природе трудны для понимания неспециалистами (и новичками). Многие «истины», которых люди ожидают придерживаться, на самом деле не соблюдаются. Например, вычисление 1.0/3.0 + 1.0/3.0 + 1.0/3.0 с плавающей запятой не дает 1.0. Точно так же (как вы обнаружили) деление на ноль ведет себя по-другому. Если вы хотите безопасно использовать числа с плавающей запятой, вам нужно понимать подводные камни.

ИЗМЕНИТЬ, уточняя первый пункт в ответ на комментарий @Jay.

Во-первых, я намеренно употребил слово «обычно». Бывают случаи, когда предотвращение генерации ожидаемого исключения ничего не дает.

Сказав это, вы часто не хотите, чтобы общее "ожидаемое" исключение возникало, даже если ваше приложение не может немедленно справиться с ситуацией. Например, если код OP был частью чего-то большего, может быть лучше явно выбросить IllegalArgumentException (если это нарушение контракта API) или несколько проверенных UserEnteredRubbishException, если мы знаем, что это результат неправильного ввода пользователя (и есть какая-то возможность сообщить/исправить).

Тот факт, что мы "ожидаем" исключения, означает по определению, что мы знаем о нем больше, чем (скажем) сказал бы общий ArithmeticException. Если мы позволим общему исключению распространяться, блоку catch будет сложнее действовать должным образом. В этом случае, например, удаленный блок catch не мог с уверенностью диагностировать причину деления на ноль. ArithmeticException могло появиться из другой части приложения и может даже не быть делением на ноль! Способ решения — явное создание более конкретного исключения.

person Stephen C    schedule 14.02.2010
comment
Я бы поспорил с вашим первым пунктом. Если ожидается особый случай И есть что-то, что вы можете сделать, чтобы обработать его и продолжить обработку, тогда да, вы должны проверить его, а не создавать исключение. Но если, как бы ни ожидался особый случай, все, что вы можете сделать, когда это произойдет, это закричать в панике и выпустить псов войны, тогда просто создайте исключение. - person Jay; 14.02.2010
comment
Но мега то же самое на № 3 и № 4. Перехват общего исключения редко бывает продуктивным. Вы понятия не имеете, что ловите. Кстати, я мог бы добавить: Следствие: не генерируйте общие исключения. Если ваша программа обнаруживает состояние ошибки и вы хотите сгенерировать исключение, потратьте десять минут на создание для нее нового класса, расширяющего исключение. Не просто «генерировать новое исключение (у Foo отсутствовала полоса)» или что-то в этом роде. - person Jay; 14.02.2010

Ответ заключается в выводе вашей программы — она печатает «Бесконечность» для result. Это связано с тем, что Java запрещает целочисленное деление только на ноль — деление с плавающей запятой на ноль допустимо и дает Infinity.

См. документацию по Double и Float для POSITIVE_INFINITY и NEGATIVE_INFINITY константы.

person danben    schedule 13.02.2010

Только деление на ноль с целыми числами вызовет исключение ArithmeticException. Деление на ноль с помощью double или float даст бесконечность.

person Desintegr    schedule 13.02.2010

Потому что деление двух двойников возвращает бесконечность, но не выбрасывает. Вы можете использовать isInfinite (), чтобы проверить это. Однако подходящим решением (как уже отмечалось) является проверка знаменателя перед делением.

person Matthew Flaschen    schedule 13.02.2010
comment
деление возвращает NaN только в том случае, если один из аргументов равен NaN или оба равны бесконечности или оба равны нулю. Деление ненулевого числа на ноль — вопрос — приводит к положительной или отрицательной бесконечности. java.sun.com/docs/books/ jls/третье_издание/html/ - person user85421; 14.02.2010

Никто не должен ожидать, что из их кода будет выброшено RuntimeException. Этих Exception можно (и нужно) легко избежать.

person fastcodejava    schedule 14.02.2010