2010-02-18 3 views
2

У меня есть метод, который выглядит следующим образом:Невозможно определить, что бросает исключение, NullPointer

try { 
    doStuff(); 
} catch (Exception ex) { 
    logger.error(ex); 
} 

(я действительно не использовать имена методов, как DoStuff - это просто, чтобы сделать вещи легко)

в DoStuff я делаю множество вещей, среди них есть вызов метода доступа к данным (так, другой метод в DoStuff), который заканчивается следующим:

} catch (SQLException ex) { 
    logger.error(ex); 
} finally { 
    try { 
    connection.close(); 
    proc.close(); 
    results.close(); 
    } catch (Exception e) { 
    logger.error(e); 
    } //<--Exception thrown here. HUH? 
} 
return stuff; 

Когда пошагового этого кода я получаю к второй, чтобы последний фигурный скобок (отмечен комментарием), а затем подпрыгните до улова в первом блоке кода с помощью исключения NullPointer. results.close() - это то, что выполняется прямо перед ним (результаты не равны нулю). Моя IDE (NetBeans) не предоставляет трассировку стека (она показывает, что трассировка стека равна нулю) или любую другую информацию, отличную от имени исключения (из того, что я могу сказать).

Этот код работал нормально. Фактически во время работы я изменил хранимая процедура, что метод доступа к данным (где я получаю это исключение) вызывал, а затем эта ошибка начиналась (без того, что приложение вообще остановилось). С тех пор я попытался восстановить и перезапустить, но безрезультатно. Я мог бы изменить sproc назад, но я действительно хочу узнать, что это за ошибка, поскольку нет никакого смысла, что sproc даже будет частью этого, учитывая, где в коде возникает исключение.

+1

Что показывает ex.printStackTrace()? –

+3

Глупый вопрос, но «logger» null? –

+0

Я попробую попробовать, но браузер переменных показывает, что stackTrace имеет значение NULL. Также @Matt-logger действительно инициализирован в обоих методах, упомянутых выше. Во втором случае он даже не пытается ввести этот блок исключений (обычно при переходе через него он захватывает catch, а затем вводит - это идет от результатов, чтобы привязываться к улову первого метода). –

ответ

3

Ваш метод doStuff() бросает что-то другое, кроме SQLException, и его не поймают. добавьте catch (Exception e) и зарегистрируйте это исключение и посмотрите, что произойдет.

этот пример кода демонстрирует такое же поведение вы описываете:

public class TryCatchTest { 
    public static void main(String[] args) { 
     try { 
      System.out.println("foo"); 
      throw new NullPointerException(); 
     } finally { 
      try { 
       System.out.println("bar"); 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 
     } // exception thrown here 
    } 
} 
+0

Так оно и было. Благодарю. –

+2

ловли (исключение e) - это плохая практика, поймайте ожидаемые подклассы, а не –

2

Вполне возможно, что регистратор имеет значение null.

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

+0

+1: Это была моя первая мысль. – Powerlord

+0

Это была моя первая мысль, однако, это не тот случай. Фактически, регистратор фактически используется внутри того же метода до кода во втором блоке. –

3

Закрыть ресурсы в обратном порядке, в котором они были получены:

try 
{ 
    results.close(); 
    proc.close(); 
    connection.close(); 
} 
catch (Exception e) 
{ 
    logger.error(e); 
} //<--Exception thrown here. HUH? 

Я бы также рекомендовать методы, подобные этим:

public class DatabaseUtils 
{ 
    // similar for ResultSet and Statement 
    public static void close(Connection c) 
    { 
     try 
     { 
      if (c != null) 
      { 
       c.close(); 
      } 
     } 
     catch (SQLException e) 
     { 
      // log or print. 
     } 
    } 
} 
+0

Спасибо за подсказку, я сделаю это. –

+0

вы никогда не должны ловить (исключение e) и всегда ловить подкласс. –

+0

Не могу понять, почему голосует за это. Что может быть неправильным? – duffymo

2

NullPointerException не может быть брошен в линию без заявление.

Убедитесь, что файл класса, который вы выполняете, имеет ту же версию, что и исходный объект (у меня были подобные проблемы, когда неправильно настроенный путь к классам содержал класс дважды, а более ранняя версия была найдена сначала в пути к классам или перекомпилированные файлы классов для неправильного копирования в веб-контейнер, который я использовал для тестирования).

Редактировать: Как указывает emh, это также могло быть исключение, происшедшее до ввода блока finally.

+2

не совсем верно. в случае, если исключение было выбрано в doStuff(), и не было блокировки catch, чтобы поймать его, тогда путь выполнения все равно должен был выполнить блок finally до того, как исключение может быть передано вызывающему. поэтому исключение выглядит так, будто оно выбрасывается из закрывающей скобки - это последняя строка блока finally. – emh

+0

Вы правы, будете редактировать. – meriton

0

Я 99% уверен, что это происходит в драйвере JDBC. Во-первых, ваши закрытые заявления назад. Вы должны закрыть набор результатов, оператор и соединение в этом порядке.

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

Это также может быть что-то о том, как создаются результирующие наборы в сохраненном виде, такие как доступ к нему, а затем доступ к другому, а затем возврат к первому.

-1

вы должны НИКАКОЕ использовать catch (исключение e) и всегда ловить определенный подкласс класса Exception, таким образом вы знаете, что происходит там.

+1

Я не согласен, но только частично. Арендатор должен избегать исключений, с которыми вы не собираетесь заниматься, то есть, если вы не собираетесь делать с ним что-то еще. Итак, если вас не волнует, что такое исключение, просто произошло исключение, catch Exception. Просто не делайте ошибку при перехвате Exception и считаете, что это особый тип (в данном случае SQLException). В этом конкретном случае я не поймал бы исключение, если бы не собирался делать что-то другое, кроме журнала или printStackTrace(). –

+1

Я использую catch (Exception ex), когда я понятия не имею, что может быть выброшено, или просто хочу, чтобы приложение не сработало (или, по крайней мере, зарегистрировало что-то до этого). В этом случае это не вызвало конкретного исключения, которое фактически вызвало проблему. –

+0

Кроме того, иногда требуется перехват java.lang.Throwable, чтобы полностью избежать сбоев. Но, конечно, используйте это с осторожностью. –

0

Как я уже сказал в комментарии, никогда не поймайте исключение, с которым вы не хотите иметь дело. В вашем коде, предполагая, что он завершен, вы не делаете ничего интересного с исключением, и это вызывает у вас путаницу в том, где и почему происходит исключение. Если вы действительно хотите сделать больше, чем журнал или printStackTrace(), например, обернуть его исключительным доменом (например, your.package.DataAccessException или что-то еще), тогда отлично.

Сделайте что-нибудь еще, как это:


ResultSet results = null; 
CallableStatement proc = null; 
Connection connection = null; 
try { 
    connection = > 
    proc = connection.createCallableStatement(..); 
    results = proc.execute(..); 
    > 
} finally { 
    try { 
    if (results != null) { 
     results.close(); 
    } 
    } catch (Exception e) { 
    logger.error(e); 
    } 
    try { 
    if (proc != null) { 
     proc.close(); 
    } 
    } catch (Exception e) { 
    logger.error(e); 
    } 
    try { 
    if (connection != null) { 
     connection.close(); 
    } 
    } catch (Exception e) { 
    logger.error(e); 
    } 
} 

А потом в вызывающем:


    try { 
     doStuff(); 
    } catch (SQLException e) { 
     throw new your.package.DataAccessException(e); 
     // or just let the SQLException propagate upward 
    } catch (Exception e) { 
     throw new your.package.AppException("omg, crazy pills!", e); 
     // or, once again, just let the exception 
     // propagate and don't catch anything 
    } 

Так, вынос:

  1. не протоколировать исключение, где они происходят , просто передайте их, вложенные в другое исключение. Вы не хотите, чтобы ваш процесс не знал, выполнено ли действие SQL или нет. Вы предпочли бы остановиться и сделать что-то еще.
  2. Исключения гнезд до тех пор, пока не дойдете до конца линии, вы всегда будете иметь полную трассировку в том месте, где вы хотели иметь дело с исключением, а не в пяти местах, разбросанных по всему журналу вашего сервера.
  3. Исключения для гнезд (да, я сказал это дважды!), Так что вам все равно, где JVM на самом деле выдает исключение, у вас есть следующее исключение, говорящее вам, что это фактически вызываемый оператор или неправильное закрытие ваших ресурсов и т. д.
  4. Не гнездайте и не исключайте исключения из ошибок, попавших в ваш код finally, что будет мешать оригинальному исключению, и это будет более интересно, чем неспособность закрыть и утверждение, которое не открывается в первую очередь.
  5. Установите переменные на null, прежде чем использовать их, а затем установите флажок «null» перед close().
  6. Лови каждую проблему в finally блоке по отдельности, так как вы не можете быть в состоянии закрыть ResultSet (потому что какая-то ошибка выполнения приводившая не открывать в первую очередь), но вы все равно должны попытаться закрыть CallableStatement и Connection, поскольку это, вероятно, не затронуто и по-прежнему будет вызывать утечку ресурсов.

Надеюсь, что это поможет.

Смежные вопросы