Подтвердить что ты не робот

Почему порядок имеет значение при перехвате исключений?

Мне пришлось ответить на этот вопрос некоторым кодом:

Предположим, что я написал следующую спецификацию метода:
public void manipulateData ( ) throws java.sql.SQLException, java.sql.SQLDataException

Вы пишете код для базы данных программа, которая будет использовать этот метод, и вы хотите обрабатывать каждый в частности. Что должно выглядеть предложение try/catch?
Вы можете использовать no-ops - пустые блоки {} - для содержимого предложения catch.
Здесь нас интересует только синтаксис и структура операторов.

Я ответил:

try {

} catch(java.sql.SQLException e) {

}
catch(java.sql.SQLDataException e) {

}

Он не принял ответ по этой причине:

"Ваши предложения об ущемлении находятся в неправильном порядке. Можете ли вы объяснить, почему этот порядок имеет значение?"

Правильно ли он в своем ответе?

4b9b3361

Ответ 1

Да, он прав. Как вы можете видеть в Javadoc, SQLDataException является подклассом SQLException. Поэтому ваш ответ неверен, так как он создаст блок недостижимого кода во втором catch.

В Java этот код даже не компилируется. В других языках (например, python) такой код создавал бы тонкую ошибку, потому что SQLDataException фактически был бы пойман в первом блоке, а не во втором (как если бы это был не подкласс).

Если бы вы ответили catch(java.sql.SQLException | java.sql.SQLDataException e) { }, это все равно было бы неверным, так как вопрос запрашивает конкретное обращение с каждым исключением.

Правильный ответ находится в Grijesh answer

Ответ 2

В Java вы должны сначала включить исключение с минимальным включением. Следующие исключения должны быть более инклюзивными (когда они связаны).

Например: если вы сначала включите все (исключение), то следующие никогда не будут вызываться. Код вроде этого:

try {
     System.out.println("Trying to prove a point");
     throw new java.sql.SqlDataException("Where will I show up?");
}catch(Exception e){
     System.out.println("First catch");
} catch(java.sql.SQLException e) {
     System.out.println("Second catch");
}
catch(java.sql.SQLDataException e) {
     System.out.println("Third catch");
}

никогда не распечатает сообщение, которое вы ожидаете от него.

Ответ 3

SQLDataException никогда не пострадает, поскольку SQLException поймает любые исключения SQL, прежде чем они достигнут SQLDataException.

SQLDataException является подклассом SQLException

Ответ 4

Подумайте о иерархиях наследования Исключений: SQLDataException extends SQLException Итак, если вы сначала поймаете "общий" (то есть самый верхний базовый класс иерархии), то каждая вещь "ниже" имеет тот же тип из-за полиморфизм, т.е. SQLDataException isa SQLException

Таким образом, вы должны запечатлеть их в порядке снизу w.r.t. иерархия наследования, то есть подклассы сначала всего пути (общего) базового класса. Это происходит потому, что предложения catch оцениваются в том порядке, в котором вы их объявили.

Ответ 5

Задать вопрос при перехвате исключений по следующей причине:

Помните:

  • Базовая переменная класса также может ссылаться на объект дочернего класса.
  • e является ссылочной переменной
catch(ExceptionType e){
}

Символ нижнего регистра e является ссылкой на объект throw (и catch) ExceptionType.

Причина, почему ваш код не принят?

Важно помнить, что подкласс класса должен присутствовать перед любыми их суперклассами. Это связано с тем, что оператор catch, который использует суперклассы, поймает исключение этого типа плюс любой из его подклассов. Таким образом, подкласс никогда не будет достигнут, если он появится после его суперкласса.
Кроме того, в Java, Недоступный код является ошибкой.

SQLException является supperclass SQLDataException

   +----+----+----+
   | SQLException |  `e` can reference SQLException as well as SQLDataException
   +----+----+----+
          ^
          |
          |
+----+----+----+---+
| SQLDataException |   More specific 
+----+----+----+---+

И если ваша запись похожа на ошибку Unreachable code (читать комментарии):

try{

} 
catch(java.sql.SQLException e){//also catch exception of SQLDataException type 

}
catch(java.sql.SQLDataException e){//hence this remains Unreachable code

}

Если вы попытаетесь скомпилировать эту программу, вы получите сообщение об ошибке, в котором говорится, что первый оператор catch будет обрабатывать все ошибки на основе SQLException, включая исключение SQLDataException. Это означает, что второй оператор catch никогда не будет выполняться.

Правильное решение?

Чтобы исправить это, измените порядок операторов catch. Это следующее:

try{

} 
catch(java.sql.SQLDataException e){

}catch(java.sql.SQLException e){

}

Ответ 6

Для компилятора несколько операторов catch выглядят как if..else if..else if..

Итак, с того момента, когда компилятор может сопоставить генерируемое исключение (прямое или неявное преобразование типа), он не будет выполнять последующие инструкции catch.

Чтобы избежать этого неявного преобразования типов, вы должны сохранить более общее исключение. Более выведенные должны быть указаны в начале его выписок, и большинство общих данных должны указывать на последние выписки.

Исключение SQLDataException происходит из SQLException, которое является стажером, вызванным Exception. Таким образом, вы не сможете выполнить какой-либо код, написанный в catch(java.sql.SQLDataException e){} этом блоке. Компилятор даже флагов для этой ситуации, что его мертвый код и не будет выполнен.

Ответ 7

Когда исключение происходит в методе, проверяется специальная таблица исключений метода, она содержит записи для каждого блока catch: тип исключения, начальную команду и команду окончания. Если порядок исключения неверен, некоторый блок catch будет недоступен. Конечно, javac может сортировать записи в этой таблице для разработчиков, но это не так.

Спецификация JVM: 1 и 2

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

Пока первое исключение является родительским для второго, второй блок становится недоступным.

Ответ 8

Спецификация языка Java §11.2.3 объясняет эту ситуацию:

Это ошибка времени компиляции, если предложение catch может быть проверено класс исключения E1 и предыдущее предложение catch немедленно вложенный оператор try может поймать E1 или суперкласс E1.

Версия на английском языке:

Более общие исключения должны быть после определенных. Более общие исключения должны быть после определенных.