Можно ли игнорировать Exception?
Здравствуйте. У меня выскакивает Exception, но роли никакой он не играет для меня, то есть ничего мне не портит. Выскакивает:
«Exception in thread» AWT-EventQueue-0 «java.lang.StringIndexOutOfBoundsException: String index out of range: -1» на нажаття кнопки backspace.
Вопрос: можно ли оставить данный Exception необработаным? И вообще допустимы ли подобные случаи в проектах?
Отслеживать
11 1 1 золотой знак 2 2 серебряных знака 8 8 бронзовых знаков
задан 15 фев 2015 в 22:53
20 3 3 бронзовых знака
> Вопрос, можно ли оставить данный Exception не обработаный? Можно ли оставить ошибку в программе? > и вообще допустимые подобные случаи в проектах? Физически — да Морально — нет
16 фев 2015 в 2:30
Исключение это не ошибка. Некоторые исключения можно игнорировать и глотать, но только таких примеров мало. Например, я не понимаю, зачем из некоторых методов close() бросаются исключения, а из некоторых нет.
16 фев 2015 в 4:11
хуже чем необработанные исключения только убийство маленьких котят
16 фев 2015 в 13:57
3 ответа 3
Сортировка: Сброс на вариант по умолчанию
Сначала разберитесь, откуда и почему возникает данное исключение. Дальше, если возможно, поправьте свой код так, чтобы исключение не возникало. Если это вдруг баг стороннего компонента или это исключение действительно не аффектит логику исполнения программы, то его можно «проглотить».
Но вообще необработанные исключения недопустимы. Необработанное исключение в общем случае должно быть перехвачено, залогировано, и, если, это некритическое исключение, работа пользователя должна быть продолжена. Но при этом каждое необработанное исключение — хороший сигнал для разработчика, что где-то недостаточно эффективно написана обработка ошибок. С каждым таким случаем опять же нужно разбираться и по возможности обрабатывать исключение ближе к месту возникновения.
Отслеживать
ответ дан 16 фев 2015 в 13:17
25.1k 4 4 золотых знака 45 45 серебряных знаков 81 81 бронзовый знак
Собственно, здесь ответ: «String index out of range: -1 на нажаття кнопки backspace.» При нажатии backspace позиция курсора должна сместится на -1. Если строка пустая, то из нулевой позиции получится минус первая. Сам AWT я давно не смотрел, но, видимо, ничего страшного. Просто в секции catch не надо ставить e.printStackTrace() , оставь пустой. В IDEA это принято оформлять так:
catch (xxxxException ignored)
Именно ignored , тогда она не матюкается на необработанный Exception. Фишка в том, что, при желании, ты можешь ловить этот Exception для собственной реакции на backspace в пустой строке, к примеру, переключить фокус на другое поле.
Вы всё ещё ловите исключения? Тогда мы к вам
Как знают многие разработчики, обработка ошибочных ситуаций — архиважная составляющая любого полноценного кода, претендующего на использование в реальной жизни.
И как следствие, писать обработку ошибок — прямая обязанность любого ответственного программиста. Некоторые считают, что если вы не предусмотрели все варианты поведения — вы не программист, и диплом вам не дадут (если речь о студентах).
Но с другой стороны, обрабатывать ошибки всегда лениво и напряжно. Поэтому существует много инструментов для облегчения этой задачи. Стандартный механизм обработки ошибок в Java — Exceptions. Я не буду сейчас расписывать скучные описания, как бы отвечая на скучные вопросы со многих собеседований. Скажу лишь, что Исключение — это на мой взгляд, неправильный перевод понятия Exception. Я считаю, что исключение, то есть исключительная ситуация — это про другого представителя летающих монстров, java.lang.Error.
А Exception — это не исключение, это часть правила. Это всегда один из возможных исходов. Я бы перевёл этот класс не иначе как Отклонение (Отступление, Отвод). В смысле отклонение от прямого курса. Потому как перехватив это отклонение, можно курс выправить и продолжить работу. А Исключение — это для безответственных разработчиков.
Так вот. Давайте теперь перехватывать эти отклонения. Как можно это сделать?
В современной разработке существует тенденция наследовать все Exception от непроверяемых исключений. Это позволяет не заботиться о написании кода обработки ошибок. Я считаю этот подход расхлябанным и безответственным. И сам всегда пропагандирую использование явно заданных и объявленных отклонений с жёсткой обязанностью их обработать, т.е. настаиваю на использовании только проверяемых исключений/отклонений в любом бизнес-коде.
Это вносит неудобства, да. Надо их везде ловить. Как упростить задачу? Обычно советуют тупо оборачивать исключение в java.lang.RuntimeException. Но такой подход чреват тяжёлыми последствиями, когда приложение выходит в релиз и на бой. Я как человек, имеющий ещё и плюсовый бэкграунд, прекрасно знаю, что некоторые ситуации невозможно предугадать даже очень внимательно вглядываясь в код. А потом искать их причины днями, неделями, иногда месяцами. Потому что кто-то в самом начале, в месте возникновения ошибки не объявил о возможности их возникновения.
Ну да ладно. Не будем о грустном. Лучше попробуем сделать нашу жизнь проще, написав утилитку, которая с одной стороны, позволяет обрабатывать ошибки. А с другой стороны не привносит глобальных неудобств и не захламляет код бесконечными (часто вложенными друг в друга) блоками try-catch-finally.
- Во-первых, на каждом уровне-слое приложения должна быть своя логика по контролю ошибок, специфичная для данного слоя. На каждом таком слое есть своё семейство Исключений, желательно проверяемых.
- Исключением из правила (простите за каламбур) могут будут только специализированные фреймворки, и то не всегда. Они могут игнорировать ошибки, которые от них не зависят. Но лучше делать это через туннелирование ошибок, то есть обернуть на влёте — развернуть на вылете.
- Во-вторых, надо бы упростить синтаксис обработки ошибок. Тут я поймал себя на мысли, что необходимость в этом возникает, так как мэйнстрим постепенно, медленно, но всё-таки верно потихоньку уползает от практик процедурного программирования. А блоки try-catch, по-моему, являются наследием именно этой парадигмы. И ООП , и тем более функциональщина и даже стримы Java-8 как-то плохо сочетаются с этими блоками, фигурными скобками.
Вот я и решил сделать утилитку, которая бы помогла реализовать вышеназванный подход. Предлагаю вашему вниманию её первую версию.
Здесь я попробовал сразу около трёх подходов реализовать.
Первый — просто статичная обёртка над небезопасным кодом, которая напечатает в лог и в случае чего прокинет ошибку своего местного разлива. Это метод errorToWarn .
Второй имел целью дальнейшее снижение шаблонного кода: вынесение инициализации логгеров в одно отдельное место. Для работы нужна только одна статическая функция err2Warn .
И затем я решил пойти ещё дальше и сделать описание ошибок в удобном формате, одновременно предлагая ленивую логику и флюент (как по-русски это?) интерфейс. Это вылилось в вызов, который уже возвращает своеобразный билдер, который затем можно настраивать по типу jmock или spring security api. Выглядеть это стало примерно как табличка состояний, примерно вот так:

К слову первый подход вылился во что-то наподобие

Надеюсь мои лирические рассуждения покажутся вам полезными. Возможно вам понравится использованный мной подход и вы захотите развить утилиту советом / личным вкладом.
Исключения в лямбда-выражениях Java

В Java есть проверяемые исключения (checked exceptions), которые нужно указывать в сигнатуре метода, и непроверяемые (unchecked exceptions) — не требующие этого. По сравнению с более ранними подходами концепция проверяемых исключений казалась революционной. Но с течением времени стало понятно, что эта идея не прижилась, и Java остался единственным распространенным языком программирования с проверяемыми исключениями. Например, в Kotlin все исключение непроверяемые.
Да и новые фичи Java не используют проверяемые исключения: в сигнатуре стандартных функциональных интерфейсов исключения не указаны. Использование лямбда-выражений в легаси коде часто приводит к громоздкому коду. Особенно это заметно в Stream API.
В этом посте я хотел бы рассмотреть варианты решения этих проблем.
Пример проблемного кода
Рассмотрим следующий пример кода:
Stream.of("java.lang.String", "ch.frankel.blog.Dummy", "java.util.ArrayList") .map(it -> new ForNamer().apply(it)) // 1 .forEach(System.out::println);
- Этот код не компилируется — возникает ошибка о необходимости обработки проверяемого исключения ClassNotFoundException
Для решения этой проблемы добавим try/catch.
Stream.of("java.lang.String", "ch.frankel.blog.Dummy", "java.util.ArrayList") .map(it -> < try < return Class.forName(it); >catch (ClassNotFoundException e) < throw new RuntimeException(e); >>) .forEach(System.out::println);
Но добавление try/catch совсем не улучшает читаемость кода.
Инкапсулируем try/catch в класс
Для улучшения кода сделаем небольшой рефакторинг и создадим новый класс. IntelliJ IDEA даже предлагает использовать record:
var forNamer = new ForNamer(); // 1 Stream.of("java.lang.String", "ch.frankel.blog.Dummy", "java.util.ArrayList") .map(forNamer::apply) // 2 .forEach(System.out::println); record ForNamer() implements Function> < @Override public Classapply(String string) < try < return Class.forName(string); >catch (ClassNotFoundException e) < return null; >> >
- Создаем record.
- Используем ее.
Попробуем использовать Lombok
Project Lombok — это процессор аннотаций, который генерирует дополнительный байт-код во время компиляции. Мы можем получить нужный результат, используя всего лишь одну аннотацию, без написания лишнего кода.
Project Lombok — библиотека Java, которая интегрируется с вашим IDE и инструментами сборки, улучшая вашу Java. Больше нет необходимости писать очередной геттер или метод equals. Используя одну аннотацию, вы можете получить полнофункциональный builder, автоматизировать логирование и сделать многое другое.
Например Lombok-аннотация @SneakyThrow позволяет использовать проверяемые исключения без объявления их в сигнатуре метода. Но в настоящее время она не работает для Stream API. На GitHub есть соответствующий issue.
Commons Lang спешит на помощь
Apache Commons Lang — проект с давней историей. В свое время он был весьма популярен, предлагая утилиты, которые могли бы быть частью Java API, но не были. Гораздо удобнее использовать готовое решение, чем заново изобретать DateUtils и StringUtils в каждом проекте. При написании этого поста я обнаружил, что проект по прежнему жив, и там есть на что посмотреть. Например, Failable API.
Этот API состоит из двух частей:
- Обертка вокруг Stream .
- Конвейерные методы, сигнатура которых допускает исключения.
Вот небольшой фрагмент:

Код, наконец, становится таким, каким мы ожидали увидеть его с самого начала:
Stream stream = Stream.of("java.lang.String", "ch.frankel.blog.Dummy", "java.util.ArrayList"); Failable.stream(stream) .map(Class::forName) // 1 .forEach(System.out::println);
Исправление ошибок компиляции недостаточно
Предыдущий код бросает в рантайме ClassNotFoundException , обернутый в UndeclaredThrowableException . Все компилируется без ошибок, но мы не можем определить нужное поведение:
- Выбросить первое исключение.
- Проигнорировать исключения.
- Собрать классы и исключения, чтобы обработать их на последнем этапе конвейера.
- Сделать что-то другое.
Для этого мы можем использовать силу Vavr. Vavr — это библиотека, которая привносит мощь функционального программирования в язык Java:
Vavr — это функциональная библиотека для Java. Она помогает уменьшить объем кода и повысить ошибкоустойчивость. Первый шаг к функциональному программированию — начать думать иммутабельными значениями. Vavr предоставляет иммутабельные коллекции и соответствующие функции с управляющими структурами для работы с ними.
Представьте, что нам нужен конвейер, который собирает как исключения, так и классы. Вот выдержка из API, описывающая компоненты, которые можно использовать для этого:

Stream.of("java.lang.String", "ch.frankel.blog.Dummy", "java.util.ArrayList") .map(CheckedFunction1.liftTry(Class::forName)) // 1 .map(Try::toEither) // 2 .forEach(e -> < if (e.isLeft()) < // 3 System.out.println("not found:" + e.getLeft().getMessage()); >else < System.out.println("class:" + e.get().getName()); >>);
- Оборачиваем вызов в Vavr Try .
- Преобразуем Try в Either, чтобы сохранить исключение. Если не нужно, то можно использовать Optional.
- Действуем в зависимости от того, содержит ли Either исключение (left) или ожидаемый результат (right).
Мы все еще остаемся в мире Java Stream. Все работает, как и ожидается, но forEach выглядит не очень красиво.
Vavr предоставляет собственный класс Stream , который имитирует Stream из Java Stream API, но с дополнительной функциональностью. Давайте воспользуемся им, чтобы переписать пайплайн:
var result = Stream.of("java.lang.String", "ch.frankel.blog.Dummy", "java.util.ArrayList") .map(CheckedFunction1.liftTry(Class::forName)) .map(Try::toEither) .partition(Either::isLeft) // 1 .map1(left -> left.map(Either::getLeft)) // 2 .map2(right -> right.map(Either::get)); // 3 result._1().forEach(it -> System.out.println("not found: " + it.getMessage())); // 4 result._2().forEach(it -> System.out.println("class: " + it.getName())); // 4
- Разделяем Stream из Either на кортеж из двух Stream .
- Преобразуем левый Stream из Either в Stream из Throwable .
- Преобразуем правый Stream из Either в Stream из Class .
- Далее делаем все, что нам нужно.
Выводы
Изначально в Java широко использовались проверяемые исключения. Однако эволюция языков программирования показала, что это была плохая идея.
Java Stream API плохо работает с проверяемыми исключениями. Код, использующий проверяемые исключения совместно со Stream API выглядит не очень хорошо. Для улучшения читаемости кода, ради чего мы и используем Stream API, можно использовать Apache Commons Lang.
Часто необходимо исключения обрабатывать, а не игнорировать их или останавливать пайплайн. В этом случае можно использовать библиотеку Vavr, которая предлагает более функциональный подход.
Исходный код примеров можно найти на GitHub.
Скоро состоится открытый урок «Архитектурные концепции построения систем обмена сообщений», на котором рассмотрим стили интеграции (File Transfer, RPI, Shared Database, Messaging), а также основные концепции обмена сообщениями. Регистрируйтесь по ссылке.
Как игнорировать исключения в java
Игнорировать исключения не стоит, даже если есть уверенность, что такой кейс невозможен, это может привести к неожиданному поведению в будущем. Но если очень хочется, можно сделать это с конструкцией try-catch , где в блоке catch нет логики:
try int number = Integer.parseInt("zyb"); // Будет выброшено исключение NumberFormatException > catch (NumberFormatException e) // Здесь мы его ловим, но не обрабатываем, как будто его и не было > // Программа мало того, что скомпилируется, так еще и "не упадет" в процессе выполнения.
Опять же так делать не стоит. Исключение можно обработать и залоггировать, например, с помощью фреймворка Log4j , добавив зависимость в Gradle .
// Получаем объект класса Logger, указывая класс, где будем отслеживать исключения Logger logger = LogManager.getLogger(Main.class.getName()); try int number = Integer.parseInt("zyb"); // Будет выброшено исключение NumberFormatException > catch (NumberFormatException e) // Здесь мы его ловим и обрабатываем // Логгируем наше исключение logger.error(e); // => 17:05:17.192 [main] ERROR Main - java.lang.NumberFormatException: For input string: "zyb" >
Также мы можем пробросить исключение дальше, теперь задача по обработке этого исключения будет у метода (или другого человека), вызвавшего данный метод с исключением:
// Пробрасываем исключение дальше public static void run() throws NumberFormatException int number = Integer.parseInt("zyb"); >
