Почему методу main() разрешено объявлять исключения?

#java #exception #throws

#java #исключение #выдает

Вопрос:

«Обрабатывать или объявлять. Это закон.» — Сначала голова

Но хороший ли это закон? Позвольте мне сначала привести пример:

 public static void main(String[] args) throws Exception {
    m1();
}

static void m1() throws Exception{
    m2();
}

static void m2() throws Exception {
    throw new Exception();
}
  

m2() генерирует исключение и m1() вызывает m2() , что означает, что он должен либо обработать, либо объявить его. Хммм, давайте объявим это. Затем main() вызывается m1() , и у него тот же опрос: объявить или обработать. Я снова решил объявить его, и код компилируется просто отлично.

Хорошо, это работает, но кто вообще обработал это исключение? Похоже, никто этого не сделал. Я знаю, что я новичок, но мне не нравится, как это звучит. Да, некоторые методы могут решать, объявлять или обрабатывать исключения, но почему main() ? Разве метод main не должен быть тем, который просто обрабатывает? Таким образом, никакое исключение не может «проскользнуть».

Я что-то упускаю из виду? Я был искренне удивлен, что для метода main нормально просто объявлять исключение, зная, что это последнее место, где мы могли бы технически что-то перехватить.

Комментарии:

1. Исключения предназначены для непредвиденных ситуаций, когда состояние программы неизвестно. Если вы знаете об этом, вы можете обработать исключение. В противном случае лучше, чтобы программа завершила работу с ошибочными данными.

2. Он переходит к обработчику исключений потока по умолчанию, который существует в любом случае, потому что непроверенные исключения также могут возникать. Считается, что лучше всего обрабатывать исключение само по main себе, но вы можете просто оставить его обработчику исключений по умолчанию.

3. @NomadMaker, я не согласен с тем, что исключение означает «состояние программы неизвестно». Исключение просто означает «я не смог сделать то, что от меня просили»… в общем, возьмите имя процедуры и добавьте «я не смог …» в начале ее… Например, «ProcessFile» должен выдавать исключение, если он не может обработать файл. «ValidateLogin» должен not выдавать исключение, если был введен неправильный пароль… в этом случае он смог проверить пользователя, а именно, что не удалось войти в систему. Однако, если пользовательская база данных недоступна, она should выдает исключение.

4. @JoelFan Если исключение обрабатывается близко к источнику, вы можете быть правы. Однако, если исключение было отброшено до самого main() , то есть большая вероятность, что восстановить то, что произошло, будет невозможно. Однако, если метод не смог выполнить то, что было запрошено, тогда он должен возвращать значение ошибки, а не генерировать исключение.

5. Никто не обрабатывает это -> сбой программы. Чем быстрее вы перехватываете, тем меньше кода останавливается

Ответ №1:

кто вообще обработал это исключение?

Среда выполнения Java сделала это.

Более конкретно, UncaughtExceptionHandler did, как указано в спецификации языка Java (JLS), раздел 11.3. Обработка исключения во время выполнения:

Если не catch найдено предложения, способного обрабатывать исключение, то текущий поток (поток, который столкнулся с исключением) завершается. Перед завершением выполняются все finally предложения, и неперехваченное исключение обрабатывается в соответствии со следующими правилами:

  • Если в текущем потоке установлен обработчик неперехваченных исключений, то этот обработчик выполняется.

  • В противном случае метод uncaughtException вызывается для ThreadGroup того, который является родительским для текущего потока. Если параметр ThreadGroup и его родительские ThreadGroup элементы не переопределяются uncaughtException , uncaughtException вызывается метод обработчика по умолчанию.

Итак, по умолчанию, когда main() генерируется исключение, не отмеченное или отмеченное, встроенный по умолчанию «обработчик неперехваченных исключений» просто выведет System.err трассировку стека, как если бы перед вызовом было выполнено следующее main() :

 Thread.setDefaultUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler());
  
 class DefaultUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        e.printStackTrace();
    }
}
  

После вызова «неперехваченного обработчика исключений» поток завершается, так же, как если бы вы просто возвращались из main() метода, и если код не запустил не-демонические потоки, которые все еще выполняются, программа завершается.

Комментарии:

1. Технически трассировка стека отправляется в System.err, а не в System.out .

2. @VGR Я действительно думал об этом, когда набирал его, но мои пальцы все еще печатали out . Проклятая мышечная память, или как там это называется. Привычка? <вздох>

Ответ №2:

Приложение может содержать более одного класса с методом main. В этом случае приложение должно объявить манифест, чтобы узнать, какой main является точкой входа (метод первого вызова). Основной метод может быть вызван из других методов или другого основного метода как статический метод и может генерировать любое исключение. Если вы не перехватываете исключение хотя бы в точке входа, то исключение возвращается из вашего приложения на виртуальную машину Java, а затем машина решает, что делать с исключением. Обычно jvm выводит сообщение об ошибке и возвращает операционной системе значение, отличное от 0.