Всем привет! Заметил такую вещь:
Как все мы прекрасно знаем существует 2 вида Exception:
Вопрос: Почему RuntimeException (unchecked), наследуется от класса Exception, который является (checked)?
UPGRADE: Исключения необходимо явно объявлять с помощью оператора throws, объявляются все исключения, кроме RuntimeException и Error потому что они UNCHECKED? Как понять явно объявлять?
Для того, чтобы конструкция:
catch(Exception e) {
...
}
отлавливала и RuntimeException
.
@Grundy уже объяснил, что RuntimeException
явно прописан в спецификации и то, что данная иерархия была выбрана авторами Java и только они смогут достоверно объяснить, почему был выбран именно такой вариант, а не какой-либо другой.
Я попробую объяснить почему выбранная иерархия имеет смысл.
Начнем издалека. Есть базовый класс Throwable
. Все методы, связанные с выбрасыванием исключений, определены в нем. Его наследники, что Error
, что Exception
, не объявляют никаких специфичных методов. По сути это один и тот же класс продублированный с разными названиями.
Такая иерархия явно отделяет фатальные системные ошибки (Error
) от нефатальных исключений (Exception
). В теории, разработчик должен обрабатывать только исключения, а при возникновении ошибок, программа должна прекращать работу. Например, такой блок try:
try {
//какой-то код
} catch(Exception e) {
//обрабатываем исключение
}
Нормально обработает NullPointerException
и продолжит работу. Но если возникнет OutOfMemoryError
, то исполнение прекратится и ошибка будет выброшена на уровень выше.
Разработчики Java приняли решение, довольно спорное, что код должен явно обрабатывать все исключения, которые могут возникнуть. Т.е. если код обращается к методу, который может выбросить исключение, то его нужно либо явно обработать с помощью блока try-catch:
try {
//метод, который может выбросить IOException
throwingMethod();
} catch(IOException e) {
//явно обрабатывается
}
либо явно объявить в вызывающем методе с помощью throws:
//Так мы объявляем всему миру, что наш метод опасный
//и может выбросить исключение.
//Любой код, который вызовет метод, должен будет либо
//обработать исключение, либо передать дальше.
void myMethod() throws IOException {
throwingMethod();
}
Если не сделать ни того, ни другого, то код не скомпилируется.
Error
— не проверяется, т.к., во-первых, Error
не имеет смысла обрабатывать в большинстве случаев, и, во-вторых, ошибки обычно не относятся к какому-то конкретному методу (переполнение памяти может возникнуть в любой момент).
Отдельные виды исключений потенциально очень широко распространены. Например, NullPointerException
потенциально может возникнуть при вызове почти любого нестатичного метода:
void method(MyClass obj) {
//здесь может быть NPE
obj.method();
//и здесь может быть NPE
obj.getField().method();
//и здесь
showMessage(obj.toString());
}
Явная обработка NullPointerException
привела бы к очень неудобному коду, с огромным количеством try
catch
и throws
. Поэтому для таких исключений пришли к компромису и объявили класс RuntimeException
.
(Вопрос о том, какие исключения должны наследоваться от RuntimeException
— довольно спорный. Есть рекомендации использовать непроверяемые исключения для программных ошибок, но на практике выбор может оказаться сложным)
RuntimeException
наследуется от Exception
потому-что непроверяемое исключение это все еще исключение, а не ошибка: его можно обработать и он всегда зависит от кода, а не от сторонных, системных, факторов. И если разработчик решит обрабатывать все исключения:
catch(Exception e) {
//обработка
}
, то должны обрабатываться и возникшие RuntimeException
.
Можно ли было сделать по-другому? Да, но есть нюансы. Рассмотрим варианты.
Вариант 1. Разные классы-наследники для проверяемых и непроверяемых исключений.
Можно было бы создать отдельные базовые классы для проверяемых и непроверяемых исключений:
Exception
RuntimeException CheckedException
Но в этом случае возникает вопрос: должен ли быть проверяемым Exception
и его наследники? Если да, то класс CheckedException
теряет смысл и получаем существующую иерархию. Если нет, то теряет смысл RuntimeException
. Тогда получаем второй вариант
Вариант 2. Проверяемые исключения наследуются от непроверяемых
Exception (unchecked)
CheckedException (checked)
Такая иерархия вполне допустима, но разработчики Java решили, что базовый класс должен быть проверяемым. Это соответствует выбранной философии: исключения должны быть проверяемыми по-умолчанию и непроверяемыми только если их повсеместная проверка вызывает затруднения. С этой философией не все согласны, но имеем, что имеем.
Почему RuntimeException (unchecked), наследуется от класса Exception, который является (checked)?
По следующим причинам:
Проверяемость тех или иных классов явно прописана в спецификации и не связана с какими-либо свойствами классов-исключений. Наследование имеет чисто организационный/иллюстративный характер и не связано с наследованием свойств/методов.
Проверяемое исключение — это исключение, а не ошибка, и должно обрабатываться как исключение.
Разработчики Java приняли решение, что базовый класс исключений должен быть проверяемым.
Это явно определено в спецификации, section 11.1.1:
RuntimeException
and all its subclasses are, collectively, the runtime exception classes.
The unchecked exception classes are the runtime exception classes and the error classes.
The checked exception classes are all exception classes other than the unchecked exception classes. That is, the checked exception classes are all subclasses of Throwable other than RuntimeException and its subclasses and Error and its subclasses.
Выше в спецификации явно указывается, что checked exceptions
– это все подклассы Throwable за исключением RuntimeException
, Error
и их подклассов.
То есть RuntimeException
и Error
– это просто особые случаи для компилятора.
Перевод ответа @JonSkeet
RuntimeException
и Error
можно не проверять явно, т.е. они не должны быть обёрнуты в try-catch
или методы не должны содержать throws
для компиляции, в то время как обычные Exception
обязаны обрабатываться с помощью try-catch
или передаваться через throws
.
void neverCompiled(){
throw new Exception();
}
void compiled(){
try{
throw new Exception();
} catch(Exception e){}
}
void compiled(){
throw new RuntimeException();
}
class Complex {
void compiled1() throws Exception {
throw new Exception();
}
void compiled2() {
try{
compiled1();
} catch(Exception e){}
}
}
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Какие существуют виды рекламных бордов и как выбрать подходящий?
столкнулся с проблемой что геттер для vuex не передает значение
Вобщем, у меня есть бот на Discord, мне нужно что бы при заходе в группу этот бот кидал картинку(с зарание подготовленым фоном) с ником человека(+-...
Можно как-то закрыть видео при нажати кнопки назад в браузере, но чтобы не выйти из страницы?