Java: исключения
Почему этот код правильный:
try {
} catch(ArrayOutOfBoundsException e) {}
и это неправильно
try {
} catch(IOException e) {}
Этот код неверен, потому что в try
никто никогда IOException
бросается, но в первом корпусе также никогда не бросается ArrayOutOfBoundsException
, И первый фрагмент кода правильный. Зачем?? Могу ли я сделать свои собственные исключения также как IOException
(Надо бросать перед ловлей)?
2 ответа
ArrayIndexOutOfBoundsException
является исключением во время выполнения (так как он является дочерним java.lang.RuntimeException
), таким образом, в теории это может быть брошено куда угодно. Все исключения во время выполнения могут быть сгенерированы любым кодом, без необходимости содержать метод, содержащий объявление в throws
оговорка; таким образом, компилятор не пытается проверить, что такое исключение может фактически быть вызвано любым данным блоком кода. Это было бы невозможно в любом нетривиальном случае в любом случае; любой код, который включает по крайней мере один вызов метода для неконечного класса, может вызвать такое исключение (даже если ни один из ваших текущих классов не делает этого, во время выполнения может использоваться другой подкласс).
С другой стороны, IOException
является проверенным исключением, и поэтому может быть выдано только методами, которые явно объявили его в своем throws
пункт *.
Посмотрите эту статью Sun, чтобы узнать больше о проверенных и непроверенных исключениях. Знайте также, что это что-то вроде религиозной войны, когда люди настаивают на том, чтобы все исключения были одинаковыми с обеих сторон.
РЕДАКТИРОВАТЬ: Чтобы уточнить, в вашем первом примере компилятор, вероятно, может проверить, что AIOOBE никогда не будет выброшен. Но это не так; во-первых, потому что это может быть сделано только в таких простых случаях (например, в этом), что это не даст никакой реальной выгоды; во-вторых, потому что было бы, возможно, более запутанным, если бы вам иногда разрешалось включать "невозможные" исключения времени выполнения, а иногда нет, например:
// Preparation stuff
private void myNoop() {}
public void publicNoop() {}
public final void finalNoop() {}
// hypothetically illegal (same as your first example)
try {
// do nothing
} catch (ArrayIndexOutOfBoundsException e) {}
// hypothetically illegal (myNoop() can't be overridden)
try {
myNoop();
} catch (ArrayIndexOutOfBoundsException e) {}
// hypothetically illegal (finalNoop() can't be overridden)
try {
finalNoop();
} catch (ArrayIndexOutOfBoundsException e) {}
// legal (publicNoop() could do anything at runtime)
try {
publicNoop();
} catch (ArrayIndexOutOfBoundsException e) {}
Мне кажется странным, что изменение уровня доступа или окончательности метода (или фактически класса) внезапно изменит легальность перехвата определенных исключений времени выполнения. Особенно, если учесть, что блок catch может быть на несколько уровней выше в стеке, чем изменяемый метод...
Кроме того, наличие блока catch, который никогда не будет задействован, на самом деле безвреден. "Вот как вы имеете дело с AIOOBE, если таковой возникнет" - и это никогда не происходит во время выполнения. То же самое может произойти и с проверенными исключениями; например, Callable.call()
объявлено, чтобы бросить Exception
, но конкретная реализация, которую вы используете, может никогда не выдавать никаких исключений - так что у вас будут инструкции о том, как обрабатывать исключение, которое никогда не будет вызвано во время выполнения.
В конце дня компилятор просто указывает на расхождения: "Вы уверены, что хотите перехватить IOException, так как этот код никогда не будет запущен?" Это как статическая типизация в том, что она автоматически предупреждает вас об изменениях в интерфейсе. Существуют исключения времени выполнения, поэтому вам не нужно объявлять каждый метод NullPointerException
и т.п.
* Технически это не совсем верно, для этого есть несколько лазеек низкого уровня, но в целом это так. Исключениями являются, как правило, аномалии, артефакты и / или все равно не рекомендуется.
Чтобы ответить на ваш другой вопрос: любой класс, который расширяет Exception (или любой из большинства его подклассов), будет проверенным исключением, то есть классом, который должен быть перехвачен или объявлен как IOException.
Исключением (непреднамеренным каламбуром) является RuntimeException, который является подклассом Exception, который НЕ проверяется, и любые классы, которые вы подклассуете из RuntimeException (или его подклассов), не требуют проверки.