Чтобы ответить на ваш конкретный вопрос: ваше решение должно основываться на том, как и почему будут обрабатываться ваши исключения. Вы хотите, чтобы ваша программа была максимально надежной и с легкостью реагировала на все возможные сценарии ошибок? Тогда вы действительно должны создать класс Exception для каждой возможной ошибки, которую вы можете идентифицировать. В противном случае лучше всего решать для каждого случая индивидуально.
Существует ряд очень распространенных ошибок, которые ставят под угрозу стабильность всей вашей программы (например, ClassNotFoundException или NoSuchMethodException) - они обязательно должны обрабатываться специально. Существуют и другие ошибки, которые тесно связаны друг с другом, что приводит к аналогичным проблемам - их вполне можно сгруппировать или организовать иерархически (IOException обозначает все виды ошибок, связанных с входом или выходом, NetworkIOException обозначает только входные и выходные ошибки связанные с доступом к сети и т. д.).
Гораздо важнее, чем имя исключения и иерархия классов, на мой взгляд, то, что вы делаете с ним: где вы размещаете свои блоки try / catch? Какие записи журнала должны быть записаны для какого файла журнала? Кто должен быть уведомлен? Какие сообщения об ошибках должны быть представлены только администраторам / разработчикам? Какие ошибки следует сообщить конечному пользователю?
Есть много шаблонов для обработки исключений, отвечая на все подобные вопросы. здесь .
можно найти довольно обширную коллекцию общих и полезных шаблонов.