Я наполовину согласен с комментарием Apocalisp. Экземпляры исключения должны быть зарезервированы для случаев, когда произошла ошибка данных или обработки, но могут быть восстановлены с помощью вмешательства пользователя или системы. Экземпляры RuntimeException должны быть зарезервированы для тех случаев, когда никакое вмешательство в пределах вашего приложения не может решить проблему. Таким образом, эти два типа известны как исключения с проверкой и без проверки.
Примером Непроверенных исключений может быть, если физический ресурс (например, база данных или шина сообщений) недоступен. RuntimeException хороша в этом случае, потому что вы не планируете, чтобы ресурс был недоступен, поэтому ваша бизнес-логика не должна постоянно проверять DatabaseUnavailableException или что-то подобное. Вы можете обрабатывать исключения RuntimeException по-другому (например, AOP, отправляя электронное письмо), чтобы сообщить о сбое и позволить персоналу или службе поддержки устранить физическую проблему.
Примерами проверенных исключений могут быть плохой ввод данных, или неполный доступ к данным, или что-то, что фактически не соответствует бизнес-логике, но может быть проверено и восстановлено. Примером этого может быть то, что пользователь, которого вы искали, недоступен. Это отдельное условие в вашей бизнес-логике, которое может быть проверено и обработано (например, частью View вашего приложения, которая затем сообщает пользователю сообщение об исключении как сообщение об ошибке).
Многие фреймворки используют эту модель, и она работает лучше, чем я видел в других случаях.
При этом многие заменяют проверенные исключения возвращаемыми нулями, -1, пустой строкой или Number.MIN_VALUE. Хотя это нормально, если вы обычно не ожидаете этих значений от источника данных или метода, его, вероятно, следует представлять как исключение.