Когда это нормально, чтобы поймать NullPointerException?

Эффективная Java рекомендует, чтобы мы не catch NullPointerException. Всегда ли это правильно?

Во многих случаях ловли NullPointerException тело catch вызывает только printStackTrace().

Если я не поймаю NullPointerException и звоню printStackTrace(), как я могу проверить место, где произошел exception?

А также, если я поймаю NullPointerException и тело catch пуст, мы не можем получить информацию о стеке в это время, можем ли мы?

UPDATE

Я проанализировал статистику захвата RuntimeException в источнике google android, AOSP4.2.2_r1.2.

Есть 249 RuntimeExceptoin, а следы - статистика catch-body.

42%: throw it again as other Exceptions (RuntimeException: 33%, others: 8%)

32%: just return null or 0/false/true or other default values

14%: just call log or printstacktrace

5%: just comment like "// Fall through.", "// ignore, not a valid type.", "// system process dead", "// do nothing"

2%: empty body

3%: display error messages or send fail codes (ex. network service discovery failed: replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, NsdManager.FAILURE_INTERNAL_ERROR); )


3%: intialize or reset related variables.

В почти случаях dalvik и внешняя ручка NPE, бросая другие Исключения.

Но фреймворки обычно обрабатывают, возвращая нулевые или другие значения.

  • Считаете ли вы, что выброс других Исключений в catch-body не является плохим или хорошим управлением?

  • Если NPE встречается на более высоких уровнях (например, приложения), и разработчик подтверждает, что исключение не является критическим, потому что приложение полностью независимо, могу ли я принять для нашего разработчика игнорировать NPE, поймав?

и еще одна вещь,

Могу ли я заключить, что исходный код платформы Google для Android может быть нестабильным в аспектах RuntimeException?

Ответ 1

Эффективная java рекомендует не перехватывать NullPointerException. Всегда ли это правильно?

Почти во всех случаях это правильно.

NullPointerException обычно является результатом ошибки; то есть ваше приложение столкнулось с ссылкой на объект null в ситуации, когда оно не ожидалось, а затем попыталось его использовать. В этом случае, поскольку вы (программист) не ожидали null, почти невозможно узнать, можно ли попытаться восстановить, и/или знать, что может потребоваться для исправления. Поэтому лучше всего позволить NPE распространяться на базовый уровень, а затем рассматривать его как общую ошибку. (В приложениях "network service" может быть целесообразно вернуть ответ "служебной ошибки" и попытаться продолжить.)

Другой сценарий - это то, где вы (программист) ожидаете, что может быть доставлен null. В этом случае лучшая стратегия (почти всегда) для явного тестирования для null перед тем, как вы попытаетесь ее использовать, тем самым избегая NPE... и необходимость ее обработки. Для этого есть две причины:

  • Обработка исключений обычно дорога. Действительно, это может быть на много порядков дороже, чем тестирование на null.

  • Если вы разрешите ожидаемый NPE, а затем поймаете его, вы также можете поймать другие неожиданные NPE... и обработать их неправильно.


Обратите внимание, что я квалифицировал выше, говоря "почти всегда". Теоретически возможно иметь сценарий, в котором явные тесты для null настолько загромождают ваш код, что, по крайней мере, стоит подумать о том, чтобы позволить NPE произойти. Тем не менее, есть вероятность появления неожиданных NPE... в зависимости от кода. Таким образом, этот подход всегда потенциально уязвим.

(FWIW - я никогда не сталкивался с реальным случаем, когда это было бы хорошей идеей...)


Во многих случаях обнаружения NullPointerException тело catch вызывает только printStackTrace().

Это, вероятно, плохой код. Не делать ничего редко - это правильный способ восстановления от NPE.

Если я не поймаю NullPointerException и вызову printStackTrace(), как я могу проверить место возникновения исключения?

Вы разрешаете NPE распространяться на базовый уровень. Там вы поймаете и распечатаете (или запишите) стек для всех необработанных исключений, а затем либо выручите, либо попытайтесь восстановить... если это возможно.

А также, если я поймаю NullPointerException и тело catch пусто, мы не можем получить информацию о стеке в это время, можем ли мы?

Никогда, никогда не делай этого! Это называется "раздавливание" и опасно. (Тем более, что, как я объяснил выше, NPE может быть вызвано тем, что вы/ваш код не ожидали.)

И нет, если вы это сделаете, вы не сможете получить трассировку стека. Он ушел.


Followup

Я не ставил много доверия/веры в некоторые общие стратегии для "избежания NPE" 1. Например, такие вещи:

return (someObject != null) ? someObject.toString() : "";

всегда заставляю меня подозревать, что программист не думает о проблеме. Почему someObject a null в первую очередь?

А NPE вызван тем, что на нем есть null, где вы этого не ожидаете. Таким образом, NPE обычно являются симптомами проблемы, а не самой реальной проблемой. На мой взгляд, NPE не следует избегать. Скорее, вы должны использовать NPE для поиска и устранения основной причины неожиданного null. Код, подобный выше, который позволяет избежать NPE, мешает этой цели.

Поэтому я предпочитаю/рекомендую стратегии для избежания значений null в неожиданных местах.

  • Убедитесь, что все ссылки поле инициализируется в ненулевое значение... если не null является значимой величиной.

  • Старайтесь избегать null как значимого значения, особенно если есть альтернатива. Например, пустой String, массив нулевой длины, пустой набор, выделенный экземпляр, который означает "undefined" или что-то еще. Или, для Java 8 и более поздних версий, используйте Optional.

  • Не возвращайте null как ошибку или указание специального случая. (Выбросьте исключение или верните выделенное значение.)

  • Проверьте ранние значения для непредвиденных значений null (например, аргументы null) и выбросите NPE раньше, чем позже.

  • В тех немногих местах, где аргумент или результат null являются законными, убедитесь, что ваши javadocs документируют это четко и явно. Если документации нет, то подразумевается, что null не разрешен и не будет возвращен.

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

1 - Существует ценность в знании о местах в стандартных API Java, где null используется (или злоупотребляется) как возвращаемое значение. Например, Class.getResourceAsStream(...) или HttpRequest.getParam(...). Эти "советы для избежания документов NPE" полезны настолько, насколько они указывают на эти ловушки.

Ответ 2

NullPointerException обычно происходит из-за логических ошибок в нашем коде. Мы можем устранить NullPointerException, избегая небезопасных операций. Если какой-либо NullPointerException все еще происходит, вы можете увидеть его в StackTrace.

Например

if (Obj.getName("Sam")) {
    // only executes if Obj != null
}  

Мы можем избежать NullPointerException следующим образом

if (Obj == null){
    System.out.println("Null Obj, Can't proceed");
} else {
    Obj.getName("Sam")
}

Ответ 3

Как правило, рекомендуется не ловить NullPointerException, и пусть это будет обрабатываться JVM.

Но все зависит от того, Скажем, например: если у вас есть код типа List<Student> listStudent = getAllStudent();

здесь getAllStudent() подключается к БД и дает результат. Здесь перед выполнением какой-либо операции над объектом списка вы должны проверить, что

if(listStudent != null){ 
    //do the task. 
}else{ 
    //Show Message like no student is there instead of catching NullPointerException 
}

если вы этого не сделаете, есть вероятность, что NullPointerException может произойти, если данных нет.

поэтому лучше практиковать проверку на Nulls, но не обрабатывать ее, как

try{ 

}catch(NullPointerException e){

}

Ответ 4

Вы определенно не должны использовать пустой блок catch без ДЕЙСТВИТЕЛЬНО ВАЖНОГО. Обычно нет такой причины.

Обычно полезно проверить, является ли переменная нулевым или уловить NPE и поправить более подходящую причину. Например, если метод getCar() вернулся null, вы можете поймать NPE при попытке вызвать какой-либо метод car или проверить, является ли автомобиль null и throw RuntimeException (но только если ситуация действительно небезупречный). Таким образом, вы делаете ошибки более абстрактными и более понятными.

Ответ 5

Когда генерируется исключение, будь то какое-либо исключение, jvm ищет ближайший handler (блок catch). Если он не найден в текущем методе, он ищет обработчик в вызывающем коде и так далее. SO в основном, все calling stack ищутся снизу вверх, чтобы искать блок catch, который может обрабатывать указанное исключение. Если обработчик не найден, jvm использует обработчик по умолчанию, который вызывает e.printStackTrace()

Теперь NullPointerException - это исключение во время выполнения, которое в основном указывает на некоторую логическую ошибку в коде. Бывают моменты, когда вы may хотите его поймать. Но как thumbrule:

  • не улавливают исключения во время выполнения, они обычно указывают на логические ошибки
  • не оставляйте пустой блок catch, вы потеряете stacktrace и исключение, если поймаете его, но не действуйте по нему.
  • не просто печатайте stacktrace в стандартный outout, а затем записывайте его где-нибудь для последующего анализа, при необходимости

Ответ 6

Соглашение заключается в том, чтобы избежать исключения каких-либо исключений во время выполнения. Причина: что-то ужасно пошло не так, и вы, как разработчик, должны стремиться исправлять его тридцать минут назад.

NullPointerException является, по сути, подклассом RuntimeException. Как и другие классы, у которых есть родительский элемент RuntimeException, они считаются непроверенными - поэтому код компилируется, даже если он собирается создать NPE:

// This would appropriately return "false", if the order were reversed 
// and something was empty.
public boolean doSomething(String something) {
    String hello = null;
    if(hello.equals(something)) {
        System.out.println("Wow");
    }
}

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

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

Если вы не поместите ничего в блок catch, он молча игнорируется - плохой.

Ответ 7

Обычно NullPointerException означает логическую ошибку в использовании API или отсутствующее значение exepcted. Вот почему он рекомендовал его вернуть. Однако вы можете поймать его. Практически

Я предлагаю вам поймать, когда вы хотите указать значения по умолчанию для переданные нулевые параметры.

Например:

Ожидается, что значение тайм-аута будет настраиваться, и вы ожидаете, что пользователь должен его передать. Однако пользователь забывает его передать. В этом случае вы можете поймать NPE и записать предупреждение о том, что вы используете значение по умолчанию для параметра.

Ответ 8

NullPointerException - это ситуация в коде, в которой вы пытаетесь получить доступ/изменить объект, который еще не был инициализирован.

Это по существу означает, что ссылочная переменная объекта не указывает нигде и не ссылается ни на что, либо на "null".

Joshua bloch в effective java говорит, что:

"Возможно, все ошибочные вызовы метода сводятся к аргумент или незаконное состояние, но другие исключения используются стандартно для некоторых видов незаконных аргументов и состояний. Если вызывающий абонент проходит null в некотором параметре, для которого нулевые значения запрещены, соглашение диктует исключение NullPointerException, а не IllegalArgumentException".

Итак, если вы должны разрешить NullPointerException в некоторых местах вашего кода, убедитесь, что вы делаете их более информативными, чем обычно.

Подробнее о Эффективной обработке NPE отсюда:

Ответ 9

Вам не нужно на самом деле делать e.printstacktrace() все время, вы можете указать свое собственное сообщение с помощью журнала или System.out.println()

Ответ 10

Надежный, ориентированный на пользователя код должен попытаться поймать такую ​​ "FATAL ERROR", если есть что-то НИЧЕГО, что вы можете с этим поделать. Вы можете повторить попытку вызова функции. Возможно, это нулевое исключение связано с чем-то временным. Что-то, что на самом деле не должно генерировать исключение и должно быть лучше кодировано для временного сбоя, но которое, однако, не должно препятствовать конечному пользователю получать то, что он хочет, вместо отказа от того, что может быть длительным процессом ввода формы с учетом состояния, и только на последнем, совершенно незначительном шаге, например, при вводе предпочтений электронной почты пользователей или что-то вызывает ошибку нулевого указателя и уничтожает существующий вход пользователя.