Являетесь ли вы новичком или опытным программистом, всегда полезно ознакомиться с практикой обработки исключений, поскольку это гарантирует, что вы и ваша команда сможете справиться с ней в случае возникновения проблемы.
Обработка исключений в Java — непростая задача. Новичкам непонятна обработка исключений, и даже опытные разработчики часами обсуждают, генерировать или обрабатывать исключения.
Вот почему у большинства команд разработчиков есть собственный набор спецификаций обработки исключений. Если вы новичок в команде, вы можете обнаружить, что эти нормы сильно отличаются от норм, которые вы использовали.
Тем не менее, вот несколько рекомендаций, которым следует большинство команд. Следующие 9 наиболее важных приемов помогут вам начать работу с обработкой исключений или повысить уровень обработки исключений.
1. Очистите ресурсы в Наконец или используйте оператор Try-With-Resource
В реальной разработке ресурсы часто используются в попытке, например, InputStream, вам необходимо закрыть его после использования. В этом случае распространенной ошибкой является закрытие ресурса в конце попытки.
public void doNotCloseResourceInTry() {
FileInputStream inputStream = null; try { File file = new File("./tmp.txt");
inputStream = new FileInputStream(file); // use the inputStream to read a file
// do NOT do this
inputStream.close();
} catch (FileNotFoundException e) {
log.error(e);
} catch (IOException e) {
log.error(e);
}
}
Проблема в этой ситуации заключается в том, что пока исключение не выброшено, программа работает нормально. Весь код в попытке будет выполняться нормально, а ресурсы будут закрыты.
Однако try всегда используется по какой-то причине. Когда вы вызываете один или несколько методов, которые могут генерировать исключения или сами генерировать исключения, программа может не достичь конца попытки. Так что в итоге ресурс не закроют.
Потому что вы должны поместить весь код, очищающий ресурсы, в finally или использовать оператор try-with-resource.
использовать наконец
В отличие от try, код в finally всегда выполняется независимо от того, успешно ли выполнен код в try или после обработки исключения в catch. Так что можете быть уверены, что все открытые ресурсы будут закрыты.
public void closeResourceInFinally() {
FileInputStream inputStream = null; try { File file = new File("./tmp.txt");
inputStream = new FileInputStream(file); // use the inputStream to read a file
} catch (FileNotFoundException e) {
log.error(e);
} finally { if (inputStream != null) { try {
inputStream.close();
} catch (IOException e) {
log.error(e);
}
}
}
}
Оператор Try-With-Resource в Java 7
Вы также можете выбрать оператор try-with-resource, который более подробно описан в моей статье «Начало работы с обработкой исключений в Java».
Если вы реализуете интерфейс AutoCloseable в своем ресурсе, вы можете использовать оператор try-with-resource, что и делает большинство стандартных ресурсов Java. Если вы открываете ресурс в попытке с ресурсом, ресурс будет автоматически закрыт после выполнения кода в попытке или обработки исключения.
public void automaticallyCloseResource() { File file = new File("./tmp.txt"); try (FileInputStream inputStream = new FileInputStream(file);) { // use the inputStream to read a file
} catch (FileNotFoundException e) {
log.error(e);
} catch (IOException e) {
log.error(e);
}
}
2. Создайте более конкретное исключение
Чем более конкретное и явное исключение вы выдаете, тем лучше. Всегда помните об этом, особенно если у вас есть коллега, который не знает вашего кода, или если через несколько месяцев вам нужно будет вызывать собственный метод и обрабатывать исключения.
Поэтому вам необходимо предоставить как можно больше информации, которая облегчит понимание вашего API. Таким образом, вызывающая сторона вашего метода может лучше обрабатывать исключения и избегать дополнительных проверок, подобных этой.
Итак, вы должны найти класс, который лучше всего соответствует вашему событию-исключению, например, генерировать NumberFormatException вместо IllegalArgumentException (Примечание. Например, при преобразовании аргумента в числовое значение вы должны генерировать конкретное NumberFormatException вместо общего IllegalArgumentException ). Пожалуйста, избегайте создания неопределенного исключения.
public void doNotDoThis() throws Exception {
...
}public void doThis() throws NumberFormatException {
...
}
3. Задокументируйте свои исключения
Когда вы указываете исключение в сигнатуре метода, вы также должны задокументировать его в Javadoc.
Поэтому не забудьте добавить оператор @throws в документ Javadoc и описать условия, которые могут вызвать исключение.
/**
* This method does something extremely useful ...
*
* @param input
* @throws MyBusinessException if ... happens
*/public void doSomething(String input) throws MyBusinessException {
...
}
4. Бросьте информацию описания вместе с исключением
Идея этого метода аналогична предыдущим двум. Но на этот раз вам не нужно предоставлять информацию вызывающему методу. Для тех, кто столкнулся с ошибкой исключения и должен выяснить причину ошибки, информация об исключении всегда регистрируется одновременно с исключением или печатается на экране.
Поэтому постарайтесь быть максимально точным, поэтому лучше не использовать Throwable в catch, если вы не уверены, что находитесь в какой-то конкретной ситуации, например, когда вы достаточно хороши, чтобы самостоятельно обработать ошибку, или когда вы попросили обработать ошибку. описывать аномальные события и предоставлять наиболее важную информацию, чтобы другие могли понять, что произошло.
Не поймите меня неправильно. Вам не нужно писать длинный абзац, но вы должны объяснить, почему возникло исключение, в одном или двух коротких предложениях. Это позволяет вашей команде разработчиков понять серьезность проблемы, а также упрощает анализ сервисных инцидентов.
Если вы выдаете конкретное исключение, имя его класса, вероятно, уже описывает тип ошибки. Таким образом, вам не нужно предоставлять много дополнительной описательной информации. Хорошим примером является то, что конструктор java.lang.Long выдает NumberFormatException, когда вы предоставляете параметр String с неправильным форматированием.
try {
new Long("xyz");
} catch (NumberFormatException e) {
log.error(e);
}
Имя класса NumberFormatException уже говорит вам о типе проблемы. Таким образом, сообщение об исключении просто должно вернуть входную строку, вызвавшую проблему. Если имя класса исключения не указывает на его значение, то в сообщении об исключении также необходимо предоставить необходимую поясняющую информацию.
17:17:26,386 ERROR TestExceptionHandling:52 - java.lang.NumberFormatException: For input string: "xyz"
5. Расставьте приоритеты для определенных исключений
Большинство IDE могут сделать это за вас. Когда вы пытаетесь перехватывать менее специфичные исключения, среда IDE сообщит вам, что это недостижимый блок кода.
Причина этой проблемы в том, что будет выполнен только первый блок catch, соответствующий исключению. Таким образом, если вы сначала поймаете IllegalArgumentException, вы никогда не доберетесь до блока catch, который обрабатывает более конкретное исключение NumberFormatException, потому что NumberFormatException является подклассом IllegalArgumentException.
Итак, сначала перехватывайте более конкретные исключения, а затем добавляйте менее конкретные блоки перехвата.
Ниже вы можете увидеть такой пример оператора try-catch. Первый перехватывает всю обработку исключений NumberFormatExceptions, второй перехватывает процесс обработки исключений NumberFormatException с недопустимым аргументом вне исключений Exceptions.
public void catchMostSpecificExceptionFirst() { try {
doSomething("A message");
} catch (NumberFormatException e) { log.error(e);
} catch (IllegalArgumentException e) { log.error(e)
}
}
6. Не ловите Throwable
Throwable — это родительский класс для всех исключений и ошибок. Хотя вы можете использовать его в предложении catch, вы никогда не должны этого делать!
Если вы используете Throwable в предложении catch, он будет перехватывать не только все исключения, но и все ошибки. Эти ошибки выдаются JVM для обозначения серьезных ошибок, которые не предназначены для обработки приложением. Типичными примерами являются OutOfMemoryError и StackOverflowError, обе из которых вызваны ситуациями, не зависящими от приложения, и не могут быть обработаны.
Таким образом, лучше не использовать Throwable в catch, если только вы не можете быть уверены, что находитесь в какой-то конкретной ситуации, например, если вы способны самостоятельно обрабатывать ошибки или должны обрабатывать ошибки.
public void doNotCatchThrowable() { try { // do something
} catch (Throwable t) { // don't do this!
}
}
7. Не игнорируйте исключения
Анализировали ли вы отчет об ошибке, в котором была выполнена только первая часть кода варианта использования?
Обычно это вызвано игнорировать аномалию. Разработчики могут быть очень уверены, что это исключение не будет брошено, а затем добавить улов, который не может быть обработан или невозможно записать это исключение. Когда вы найдете этот улов, вы, вероятно, найдете такую известную аннотацию: «Это никогда не случится».
public void doNotIgnoreExceptions() { try { // do something
} catch (NumberFormatException e) { // this will never happen
}
}
Да, возможно, вы анализируете проблему, которая никогда не возникнет.
Поэтому, пожалуйста, не игнорируйте исключения. Вы не знаете, как код изменится в будущем. Некоторые люди могут по ошибке удалить проверку аномальных событий, совершенно не подозревая, что это вызовет проблемы. Или код, вызывающий исключение, изменен, один и тот же класс генерирует несколько исключений, а код, который их вызывает, не предотвращает возникновение этих исключений.
Вы должны по крайней мере распечатать информацию журнала, чтобы сообщить всем, кто невольно сделал что-то не так, чтобы проверить здесь.
public void logAnException() { try { // do something
} catch (NumberFormatException e) { log.error("This should never happen: " + e);
}
}
8. Не печатайте и не выбрасывайте исключения одновременно
Это, вероятно, наиболее часто упускаемое из виду практическое правило в этой статье. Вы можете найти эту проблему во многих фрагментах кода и даже в библиотеках, где исключения перехватываются, печатаются и повторно выбрасываются.
try { new Long("xyz");
} catch (NumberFormatException e) { log.error(e); throw e;
}
Таким образом, может быть интуитивно понятно, что исключение печатается, исключение создается повторно, и вызывающий объект хорошо с ним справляется. Но это приведет к тому, что одно и то же исключение будет распечатывать несколько сообщений об ошибках.
17:44:28,945 ERROR TestExceptionHandling:65 - java.lang.NumberFormatException: For input string: "xyz"Exception in thread "main" java.lang.NumberFormatException: For input string: "xyz"at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)at java.lang.Long.parseLong(Long.java:589)at java.lang.Long.(Long.java:965)at com.stackify.example.TestExceptionHandling.logAndThrowException(TestExceptionHandling.java:63)at com.stackify.example.TestExceptionHandling.main(TestExceptionHandling.java:58)
Дополнительная информация не дает более подробной информации об ошибке. Как указано в Правиле 4, аномальная информация должна точно описывать аномальные события. Stack Trace подскажет, в каком классе, в каком методе и в какой строке возникло исключение.
Если вам нужно добавить дополнительную информацию, вы должны перехватить и обернуть исключение в пользовательское исключение, но обязательно следуйте девятому практическому руководству ниже.
public void wrapException(String input) throws MyBusinessException { try { // do something
} catch (NumberFormatException e) { throw new MyBusinessException("A message that describes the error.", e);
}
}
Поэтому ловите исключение только тогда, когда хотите его обработать. В противном случае укажите исключение в сигнатуре метода для привлечения внимания вызывающей стороны.
9. Оберните исключения, но не отбрасывайте исходное исключение
Иногда обертывание исключения как пользовательского исключения лучше, чем перехват стандартного исключения. Типичным примером является бизнес-исключение, связанное с приложением или платформой. Это позволяет добавить дополнительную информацию, а также реализовать специальный обработчик для вашего класса исключений.
Когда вы делаете это, обязательно убедитесь, что исходное исключение установлено как причина . Класс Exception предоставляет ряд конкретных конструкторов, которые могут принимать Throwable в качестве параметра (Примечание: например,Exception(String message, Throwable cause)
). В противном случае вы потеряете трассировку стека и информацию об исходном исключении, что сильно затруднит анализ события, вызвавшего исключение.
public void wrapException(String input) throws MyBusinessException { try { // do something
} catch (NumberFormatException e) { throw new MyBusinessException("A message that describes the error.", e);
}
}
Суммировать
Как видите, при принятии решения о том, генерировать или перехватывать исключения, необходимо учитывать множество аспектов. Большинство приведенных выше практических рекомендаций предназначены для повышения удобочитаемости и удобства использования вашего кода и API.
Исключения — это не только механизм обработки ошибок, но и средство связи. Поэтому вам следует обсудить со своими коллегами, какие передовые методы и рекомендации вы хотите применить, чтобы каждый мог понять основные концепции и одинаково применять их на практике.
Рекомендуемое чтение
[Совместное использование ресурсов] Видеоруководство по базовой технологии Docker
Подробный обзор системы журналов Java
[Совместное использование ресурсов] Практический видеокурс Spring Cloud Microservices
Установка Docker и общие команды и операции
[Совместное использование ресурсов] Видеоруководство Netty
Сводка анализа исходного кода Springmvc
Схема системы Spring IOC и карта знаний АОП
Интерпретация исходного кода архитектуры MyBatis
Вы все в Intellij IDEA? Может быть, вам нужно посмотреть на этот пост в блоге
Эта официальная учетная запись время от времени будет давать вам преимущества, включая учебные ресурсы и т. д., так что следите за обновлениями!
Если push-контент сейчас не нужен для работы, вы можете сначала перенаправить его в «Моменты» или «Избранное», чтобы легко найти его при использовании.
Кроме того, открытые номера приветствуются для ответа WeChat , добавляйте друзей в WeChat, учитесь и общайтесь друг с другом.