Изящная обработка исключений — это искусство!

Java

01. Предисловие

У вас сложилось впечатление, что когда вы хотите обновить приложение, в его журнале обновлений всегда есть одно или два описания:

  • исправить некоторые ошибки
  • Убил программиста, чтобы принести его в жертву небу, и успешно решил оставленную им ошибку

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

На самом деле лидер не станет заявлять своей головой: «В нашей программе никогда не будет ошибок», но когда в программе есть ошибка, руководитель не колеблясь возложит вину на программиста.

Чтобы избавить себя от вины, мы можем сделать это:

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

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

02. Ненормальный

В Java, исключением (бросаемая) иерархия следующим образом.

Исключения класса ошибок описывают внутренние ошибки системы времени выполнения Java, такие как наиболее распространенныеOutOfMemoryErrorиNoClassDefFoundError.

Привести кOutOfMemoryErrorОбщие причины следующие:

  • Объем данных, загруженных в память, слишком велик, например, из базы данных одновременно извлекается слишком много данных;
  • Ссылка на объект в коллекции не очищается после использования, поэтому JVM нельзя перезапустить;
  • В коде есть бесконечный цикл или цикл создает слишком много повторяющихся объектов;
  • Установленное значение памяти в параметрах запуска слишком мало;

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

Привести кNoClassDefFoundErrorЕсть только одна причина: виртуальная машина Java может найти класс во время компиляции, но не во время выполнения.

NoClassDefFoundErrorРешение, я сделал скриншот, как показано выше. Помните об этом шаге, когда проект ссылается на другой проект!

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

if (str == null || "".eqauls(str)) {
}

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

Пример метания.

public static void main(String[] args) throws IOException {
	InputStream is = new FileInputStream("沉默王二.txt");
	int b;
	while ((b = is.read()) != -1) {

	}
}

Захваченный пример.

public static void main(String[] args) {
	try {
		InputStream is = new FileInputStream("沉默王二.txt");
		int b;
		while((b = is.read()) != -1) {
			
		}
	} catch (IOException e) {
		e.printStackTrace();
	}
}

03. наконец

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

В приведенном ниже примере входной поток будет отключен для высвобождения ресурсов.

public static void main(String[] args) {
	InputStream is = null;
	try {
		is = new FileInputStream("沉默王二.txt");
		int b;
		while ((b = is.read()) != -1) {}
	} catch (IOException e) {
		e.printStackTrace();
	} finally {
		is.close();
	}
}

Но я всегда чувствую, что с этим дизайном что-то не так, потому чтоclose()метод также выдастIOException:

    public void close() throws IOException {}

То есть вызовclose()Основной метод либо должен броситьIOException, либо должны быть вfinallyПовторный захват в пунктеIOException.

Выбор первого сделаетtry catchНемного неудобно, как показано ниже.

public static void main(String[] args) throws IOException {
	InputStream is = null;
	try {
		is = new FileInputStream("沉默王二.txt");
		int b;
		while ((b = is.read()) != -1) {}
	} catch (IOException e) {
		e.printStackTrace();
	} finally {
		is.close();
	}
}

При выборе последнего варианта код выглядит раздутым, как показано ниже.

public static void main(String[] args) {
	InputStream is = null;
	try {
		is = new FileInputStream("沉默王二.txt");
		int b;
		while ((b = is.read()) != -1) {}
	} catch (IOException e) {
		e.printStackTrace();
	} finally {
		try {
			is.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

Короче говоря, нам нужно другое более элегантное решение. Добавлен JDK7Try-With-ResourceСинтаксис: Если класс (например,InputStream) ДостигнутоAutoCloseableинтерфейс, то вы можете создавать объекты этого класса вtryв скобках после ключевого слова, когдаtry-catchПосле выполнения блока кода Java гарантирует, что объектcloseметод называется. Примеры следующие.

public static void main(String[] args) {
	try (InputStream is = new FileInputStream("沉默王二.txt")) {
		int b;
		while ((b = is.read()) != -1) {
		}
	} catch (IOException e) {
		e.printStackTrace();
	}
}

04. Предложения

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

1) Попробуйте поймать исходное исключение.

на самом деле должен захватитьFileNotFoundException, но фиксирует обобщенныйException. Примеры следующие.

InputStream is = null;
try {
	is = new FileInputStream("沉默王二.txt");
} catch (Exception e) {
	e.printStackTrace();
}

Недостаток этого очевиден: если вы назовете «Ван Эр», то я осмелюсь согласиться, если вы назовете «Фараон», то я действительно посмею не согласиться, а если вы назовете мою сестру «Ван Сан»?

Многие новички ошибочно думают, что захват обобщенияExceptionЭто удобнее, но и людям легче «озадачиться вторым монахом». И наоборот, перехват исходного исключения упрощает для соавторов определение типа исключения, что упрощает определение источника проблемы.

2) Старайтесь не печатать стек, а потом кидать исключение

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

public static void main(String[] args) throws IOException {
	try (InputStream is = new FileInputStream("沉默王二.txt")) {
	}catch (IOException e) {
		e.printStackTrace();
		throw e;
	} 
}

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

java.io.FileNotFoundException: 沉默王二.txt (系统找不到指定的文件。)
	at java.io.FileInputStream.open0(Native Method)
	at java.io.FileInputStream.open(FileInputStream.java:195)
	at java.io.FileInputStream.<init>(FileInputStream.java:138)
	at java.io.FileInputStream.<init>(FileInputStream.java:93)
	at learning.Test.main(Test.java:10)
Exception in thread "main" java.io.FileNotFoundException: 沉默王二.txt (系统找不到指定的文件。)
	at java.io.FileInputStream.open0(Native Method)
	at java.io.FileInputStream.open(FileInputStream.java:195)
	at java.io.FileInputStream.<init>(FileInputStream.java:138)
	at java.io.FileInputStream.<init>(FileInputStream.java:93)
	at learning.Test.main(Test.java:10)

3) Никогда не используйте механизм обработки исключений вместо суждения.

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

public static void main(String[] args) {
	try {
		String str = null;
		String[] strs = str.split(",");
	} catch (NullPointerException e) {
		e.printStackTrace();
	}
}

Отлов исключений занимает гораздо больше времени, чем оценка! Мы можем смоделировать два фрагмента кода для сравнения.

Фрагмент кода А:

long a = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
	try {
		String str = null;
		String[] strs = str.split(",");
	} catch (NullPointerException e) {
	}
}
long b = System.currentTimeMillis();
System.out.println(b - a);

Фрагмент кода Б:

long a = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
	String str = null;
	if (str != null) {
		String[] strs = str.split(",");
	}
}
long b = System.currentTimeMillis();
System.out.println(b - a);

Для 1 000 000 000 циклов время выполнения фрагмента кода A (механизм обработки исключений) занимает около 1983 мс, а время выполнения фрагмента кода B (обычное суждение) — всего около 1 мс. Такое сравнение, хотя и неточное, но достаточное, чтобы проиллюстрировать суть.

4) Не перехватывайте исключения преждевременно.

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

InputStream is = null;
try {
	is = new FileInputStream("沉默王二.txt");

} catch (FileNotFoundException e) {
	e.printStackTrace();
}

int b;
try {
	while ((b = is.read()) != -1) {
	}
} catch (IOException e) {
	e.printStackTrace();
}

finally {
	try {
		is.close();
	} catch (IOException e) {
		e.printStackTrace();
	}
}

Если файл не найден,InputStreamСсылка на объект isnull,новыйNullPointerExceptionпоявится.

java.io.FileNotFoundException: 沉默王二.txt (系统找不到指定的文件。)
	at java.io.FileInputStream.open0(Native Method)
	at java.io.FileInputStream.open(FileInputStream.java:195)
	at java.io.FileInputStream.<init>(FileInputStream.java:138)
	at java.io.FileInputStream.<init>(FileInputStream.java:93)
	at learning.Test.main(Test.java:12)
Exception in thread "main" java.lang.NullPointerException
	at learning.Test.main(Test.java:28)

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

05. Наконец

Ну, давайте поговорим об исключениях здесь. Обработка исключений — одна из важнейших операций при разработке программы, но как правильно и изящно обрабатывать исключения — это знание Хороший механизм обработки исключений может обеспечить надежность программы и повысить доступность системы.