惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Webroot Blog
Webroot Blog
罗磊的独立博客
B
Blog RSS Feed
大猫的无限游戏
大猫的无限游戏
G
Google Developers Blog
WordPress大学
WordPress大学
T
Tailwind CSS Blog
U
Unit 42
B
Blog
Stack Overflow Blog
Stack Overflow Blog
J
Java Code Geeks
Vercel News
Vercel News
博客园 - Franky
T
Tenable Blog
F
Fortinet All Blogs
P
Privacy International News Feed
P
Palo Alto Networks Blog
Security Latest
Security Latest
爱范儿
爱范儿
K
Kaspersky official blog
Engineering at Meta
Engineering at Meta
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
V
V2EX
The Cloudflare Blog
H
Help Net Security
NISL@THU
NISL@THU
酷 壳 – CoolShell
酷 壳 – CoolShell
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
The GitHub Blog
The GitHub Blog
V
Visual Studio Blog
月光博客
月光博客
C
CERT Recently Published Vulnerability Notes
L
Lohrmann on Cybersecurity
Latest news
Latest news
A
Arctic Wolf
C
Cisco Blogs
宝玉的分享
宝玉的分享
Cyberwarzone
Cyberwarzone
Y
Y Combinator Blog
O
OpenAI News
S
Security Archives - TechRepublic
www.infosecurity-magazine.com
www.infosecurity-magazine.com
I
InfoQ
云风的 BLOG
云风的 BLOG
PCI Perspectives
PCI Perspectives
C
CXSECURITY Database RSS Feed - CXSecurity.com
Recorded Future
Recorded Future
V
V2EX - 技术
D
DataBreaches.Net

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Правильное использование механизма исключений
Александр · 2026-06-15 · via Все публикации подряд на Хабре

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

В первую очередь следует понимать, что исключения — это синтаксический сахар. Реализовать обработку ошибок можно и обычными if...else, но кода будет намного больше. А если механизм исключений используется неправильно, то преимущества такого синтаксического сахара просто не используются, вы получаете тот же if...else, только обогащаете код «ненужным в этом случае синтаксисом».

Исключение — это объект, содержащий информацию о произошедшей исключительной ситуации. Информация может быть произвольной: благодаря наследованию, мы можем определять свои структуры данных для кастомных исключений.

Например, в следующем коде определяется собственное исключение, сообщающее, что ресурс не найден. По типу исключения мы понимаем его суть, а по полям user и resourceId мы понимаем, кто пытался запросить ресурс и какой именно. Имея такую информацию в логах, становится сильно проще разбираться в причинах проблемы.

public class ResourceNotFoundException extends RuntimeException {
  private final User user;
  private final String resourceId;

  public ResourceNotFoundException(String resourceId, User user) {
    super("Ресурс с id " + resourceId + " не найден. Пользователь: " + user.toString());
    this.resourceId = resourceId;
  }
}

В чем заключается синтаксический сахар исключений? А в том, что в цепочке вызовов методов не требуется прокидывать объект с информацией об ошибке. Между throw и try‑catch может быть сколько угодно промежуточных методов, которые ничего не будут знать об этом исключении.

Цепочка методов, через которые перебрасывается исключение

Цепочка методов, через которые перебрасывается исключение

Метод, организующий try‑catch, несет ответственность за обработку заданных исключений. Очень важно навешивать ответственность за обработку исключений архитектурно правильно для прозрачности кода. Более того, если метод несет ответственность за обработку исключений, он больше не должен на себя брать никакой ответственности, то есть логики в нем не должно быть.

Взглянем на следующий код:

public T execute(Object[] params) {
  try {
    Task task = generateTask();
    String id = task.id;
    if (params.size() > 0) {
      fixParams(id, params);
      return task.run(params);
    } else {
      return task.run();
    }
  } catch (Exception e) {
    log(e.getMessage());
  }
}

В этом коде прослеживается логика внутри блока try. Это затрудняет чтение особенно из‑за возникшего внутреннего условного оператора — дополнительный уровень вложенности.

А вот так должен выглядеть метод, который несет ответственность только за обработку исключений:

public T execute(Object[] params) {
  try {
    executeTask(params);
  } catch (Exception e) {
    log(e.getMessage());
  }
}

Метод, организующий try catch блок, является замыкающим: вся цепочка методов, вызываемых после него, не имеет try catch блока совсем — там реализуется исключительно бизнес‑логика. Согласитесь, намного приятнее выглядит метод прошлого примера без try‑catch блока?

public T executeTask(Object[] params) {
  Task task = generateTask();
  String id = task.id;
  if (params.size() > 0) {
    fixParams(id, params);
    return task.run(params);
  } else {
    return task.run();
  }
}

Метод должен иметь минимальное количество уровней вложенности: лишний вложенный if уже затрудняет чтение, так же как и блок try. Идеальная структура метода, несущего ответственность за обработку исключений, выглядит следующим образом:

// Реализация метода
try {
  // Вызов метода-обработчика (одна строка)
} catch (MyException e) {
  // Логика обработки исключения (любое число строк)
} catch (MyException2 e) {
  // Логика обработки исключения 2 (любое число строк)
...
} catch (MyExceptionN e) {
  // Логика обработки исключения N (любое число строк)
}

В таком методе акцент только на обработке исключений. В блоке try ничего нет кроме вызова какой‑то операции. При этом обработка отдельного исключения реализуется одним из следующих вариантов:

  • Логирование. Метод замыкает цепочку вызовов, организуя запись в лог подробной информации об ошибке. Обычно реализуется сервисами, которые не должны падать в случае ошибок, но сообщать информацию разработчикам о том, что что‑то пошло не так.

  • Переброс исключения. Метод до‑обогащает исключение дополнительной информации, которая отсутствует в пойманном исключении. К примеру, для какого ID процесса произошла проблема. Переброс недопустим, если до‑обогащать новой информацией исключение не требуется. Единственное, в редких случаях допускается пере‑выбрасывать Exception в RuntimeException, чтобы устранить лишние throws.

  • Определение другого сценария поведения. Имеется ввиду, что в блоке catch реализуется полноценная логика. Например, не удалось преобразовать строку в json, поэтому в блоке catch идем по другому сценарию, где обрабатывается чистая строка. Такой вариант обработки исключений по возможности рекомендуется исключать, а пользоваться признаками или флагами для понимания, в какой сценарий необходимо уйти. Использование такого подхода сильно усложняет код, поскольку ожидаем обработку ошибки, а в итоге уходим в другой сценарий, как будто это ветка else.

  • Формирование результата. Если метод должен в любом случае вернуть какой‑то результат независимо от исключения, тогда в блоке catch может быть реализована логика формирования специфического ответа метода в случае ошибки. Такое часто применяется для REST, когда клиент в любом случае должен получить ответ. Тот же Spring благодаря магии рефлексии обеспечивает лаконичность благодаря handler‑ам исключений: мы вообще не пишем try‑catch, мы объявляем handler — отдельный метод, который вызывается в случае заданного исключения.

Важно отметить, что крайне не рекомендуется в методе, отвечающем за обработку исключений, смешивать разные варианты из представленных 4-х. Давайте рассмотрим примеры кода для каждого варианта:

Логирование

try {
  // ...
} catch (MyException e) {
  // Логирование в случае получения кастомного исключения (пишем все доступные поля в лог)
  log(buildMessage(e.getProcessId(), e.getMessage()));
} catch (Exception e) {
  // Логирование всех остальных исключений с менее подробной информацией 
  // (исключения, которые разработчик не предусмотрел)
  log(e.getMessage());
}

Переброс исключения

try {
  // ...
} catch (ArgumentException e) {
  // Переброс исключения, до-обогащая processId
  throw new MyException(processId, e.getMessage());
}

Определение другого сценария поведения

try {
  JsonNode json = parseToJson(content);
  processJson(json);
} catch (ArgumentException e) {
  // Обработка строки
  processString(content);
}

Формирование результата

try {
  // Возвращаем успешный результат
  return ResponseEntity.body(execute(id));
} catch (Exception e) {
  // Возвращаем тело ошибки
  return ResponseEntity.body(buildError(e));
}

Для максимальной прозрачности кода рекомендуется документировать методы вместе с возможными исключениями, которые могут быть важны для пользователя. В Java мы можем использовать @throws для документации:

/**
 * Преобразовать перечень элементов в список строк
 * @param items Перечень элементов
 * @return Скомпонованный список
 * @throws NullPointerException Передан нулевой элемент или список
 * @throws IncorrectJsonException Не удается преобразовать в json элемент
 */
public ArrayList<String> flat(Iterable items, ObjectMapper mapper)

Теперь, если мы захотим вызвать этот метод, мы сразу увидим, к чему быть готовым. При этом указание исключения вовсе не означает, что надо его обрабатывать, мы можем выполнить предварительные проверки. Например, нас предупредили, что может быть NullPointerException, значит, нам надо убедиться, что наш код никак не передаст в качестве items null. То же касается и IncorrectJsonException — вероятно, мы проверки сделаем предварительно, и не придется выполнять try‑catch.

if (items != null)
  // Гарантируем, что items не равен null (try catch не нужен для NullPointerException)
  return flat(items, objectMapper);
else
  return new ArrayList<>();

Ключевое слово throws для объявляемых исключений, к сожалению, часто создает загромождение в коде. Иногда мы гарантируем, что исключение не может быть выброшено бизнес‑логикой, но ключевое слово throws все равно вынуждает нас писать try catch. В этом плане даже Роберт Мартин в своей книге «Чистый код» говорит о ненужности throws и рекомендует не работать с объявляемыми исключениями — пере‑выбрасывать в не объявляемые. Код ниже демонстрирует сигнатуру метода, который вынуждает обработать JsonProcessingException.

// Вынуждаем снаружи обрабатывать исключение JsonProcessingException, хотя наш код
// мог бы гарантировать, что такого исключения не может быть
public ArrayList<String> flat(Iterable items, ObjectMapper mapper) 
  throws JsonProcessingException

Исключения — наши помощники, когда речь идет о разработке очень крупной и сложной системы. Исключения помогают разработать стабильный код, который будет работать в любой ситуации и правильно реагировать на любые возникшие проблемы. Если вы научитесь понимать, кто из объектов и методов должен нести ответственность за обработку исключений, вы сможете строить стабильные и надежные системы произвольной сложности.

Хороших всем разработок!