Утверждение отражает состояние, которое никогда не должно происходить и не ожидалось, когда приложение не может продолжить выполнение по той или иной причине, тогда как исключение указывает состояние, которое не считается «нормальным», но не было неожиданным, и из которого можно было бы восстановить.
Например, если я выделю пространство в куче, и это распределение завершится неудачно, я не смогу продолжить работу, поэтому я утверждаю, что возвращенный адрес действителен; где оно недопустимо, утверждение не выполняется, и программа завершается с ним ошибкой.
С другой стороны, если я открою файл для чтения, а он не существует, то можно будет исправить ситуацию, в этом случае возникает исключение (и перехватывается, и обрабатывается до тех пор, пока возможно).
Как правило, утверждения наиболее полезны на этапе отладки, тогда как исключения считаются частью обычной программы и обработки ошибок. Общее мнение состоит в том, что утверждения должны быть отключены в производственном коде (чтобы защитить пользователей от явных сбоев), тогда как я прочитал научную мысль, которая утверждает, что это контрпродуктивно, и что пользователь должен увидеть сбой утверждения, чтобы они может правильно сообщить о проблеме.
Лично я иногда комбинирую эти две техники; обычно, если я ловлю исключение, которое я не верю, может быть брошено. В приведенном выше примере, если я проверяю существование файла перед попыткой его открыть, то не ожидаю, что будет сгенерировано исключение, а если оно есть, то я склонен иметь дело с этим, поднимая утверждение в соответствующем блоке catch. Я считаю этот метод особенно полезным в Java, где такие исключения полностью проверяются.