JavaRush /Java блог /Random /Кофе-брейк #220. Как исправлять исключения в Java — подро...

Кофе-брейк #220. Как исправлять исключения в Java — подробное руководство

Статья из группы Random
Источник: JavaTechOnline Это руководство поможет вам научиться исправлять распространенные исключения в Java. Помимо теории, вы увидите примеры кода по исправлению таких проблем. Кофе-брейк #220. Как исправлять исключения в Java — подробное руководство - 1Как и в других языках программирования, при написании кода Java-разработчики могут столкнуться с ошибками или исключениями. К исключениям следует относиться серьезно, поскольку их появление отнимает рабочее время. Однако, обладая некоторыми знаниями, вы можете ускорить решение большинства подобных проблем. Итак, давайте узнаем, как исправлять распространенные исключения в языке Java.

Общие рекомендации по предотвращению исключений в Java

Чтобы избежать появления каких-либо исключений в Java, важно понимать и следовать рекомендациям по кодированию:
  1. Всегда проверяйте ввод пользователя, прежде чем использовать его в своем коде.
  2. Используйте блоки try-catch, throws, throw, в зависимости от того, что подходит для конкретного сценария.
  3. Не пренебрегайте сообщениями в коде об обработках и наличии ошибок. Это поможет определить и исправить проблемы.
  4. Обязательно регистрируйте исключения в целях отладки. Вы можете использовать сообщения регистратора, чтобы помочь выявить проблемы.
  5. Следуйте рекомендациям по написанию кода, чтобы избежать исключений, и тщательно тестируйте свой код.
  6. Обновляйте свои зависимости API, чтобы убедиться, что вы используете самые последние и самые стабильные версии библиотек и фреймворков.
  7. Используйте необходимые инструменты и среды тестирования для выявления потенциальных проблем в коде до того, как они вызовут исключения.
  8. Отслеживайте производительность вашего приложения и журналы, чтобы быстро выявлять и устранять любые проблемы.
  9. Будьте в курсе передовых практик безопасности, чтобы убедиться, что ваш код безопасен и защищен от любых потенциальных атак.
  10. Тщательно документируйте свой код и его исключения, чтобы другим разработчикам было легче его понять и поддерживать.

Перечень наиболее распространенных исключений в Java

В Java существует достаточно длинный список исключений. Давайте рассмотрим некоторые из наиболее распространенных:

NullPointerException

NullPointerException (Исключение нулевого указателя) — это тип исключения RuntimeException. Оно возникает, когда мы пытаемся использовать ссылочную переменную, имеющую нулевое значение. Это означает, что она указывает на объект или переменную, чье значение равно null. Такая ошибка может произойти, если мы пытаемся вызвать метод или получить доступ к полю объекта, который не был инициализирован, или когда мы передаем null в качестве параметра методу, который его не обрабатывает. Чтобы предотвратить возникновение NullPointerException, можно проверить, является ли ссылочная переменная нулевой, используя оператор ==. Если это так, тогда нужно правильно обработать нулевой случай. В качестве альтернативы мы можем инициализировать наши переменные значениями по умолчанию (где это возможно), чтобы гарантировать, что мы никогда не столкнемся с нулевой ссылкой. Другой способ избежать исключения NullPointerException — использование класса Optional. В Java Optional — это объект-контейнер, который может содержать или не содержать ненулевое значение. Он используется для представления наличия или отсутствия значения, аналогично тому, как нулевое значение используется для представления отсутствия значения. Если программист попытается получить доступ к пустому значению объекта Optional, программа не выдаст исключение NullPointerException, а вместо этого вернет пустой объект Optional. Другими словами, Optional заставляет программиста обрабатывать случай, когда значение отсутствует, что помогает избежать исключения NullPointerException.

IndexOutOfBoundsException

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

ArrayIndexOutOfBoundsException

ArrayIndexOutOfBoundsException — это тип IndexOutOfBoundsException, возникающий при попытке доступа к массиву по недопустимому индексу. В Java индексы массива начинаются с 0 и заканчиваются на length()-1, где length() — это количество элементов в массиве. Если вы попытаетесь получить доступ к элементу по индексу за пределами этого диапазона, Java выдаст исключение ArrayIndexOutOfBoundsException. При столкновении с ArrayIndexOutOfBoundsException вам следует просмотреть свой код, чтобы убедиться, что вы используете правильные индексы при доступе к элементам в массиве.

StringIndexOutOfBoundsException

StringIndexOutOfBoundsException — это тип IndexOutOfBoundsException, возникающий при попытке доступа к символу в строке с недопустимым индексом. Как и в случае с предыдущим исключением ArrayIndexOutOfBoundsException, помните, что в Java индекс строки начинается с 0 и заканчивается на length()-1, где length() — это количество символов в строке. Если вы попытаетесь получить доступ к символу по индексу за пределами этого диапазона, вы можете столкнуться с исключением StringIndexOutOfBoundsException. Если вы столкнулись с StringIndexOutOfBoundsException, вам следует просмотреть свой код, чтобы убедиться, что вы используете правильные индексы при доступе к символам в строке.

ClassCastException

Это исключение возникает, когда мы пытаемся привести объект к типу, несовместимому с его фактическим типом. Чтобы решить подобную проблему, важно убедиться, что ваша Java-программа пытается привести объекты только к тем классам, экземплярами которых они являются. Вы можете проверить тип объекта с помощью оператора instanceof перед применением приведения.

IllegalArgumentException

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

IllegalStateException

IllegalStateException — это тип RuntimeException, который возникает, когда объект находится в неподходящем состоянии для выполняемой операции. Это может произойти, если метод вызывается для объекта, который не находится в ожидаемом состоянии. Например, если мы создадим объект подключения к базе данных, а затем закроем его. Тогда, если мы попытаемся создать объект Statement в закрытом соединении, это вызовет исключение IllegalStateException, потому что для метода createStatement() требуется открытое соединение. Если вы столкнулись с IllegalStateException, вам следует просмотреть свой код, чтобы убедиться, что вы вызываете методы для объектов, которые находятся в соответствующем состоянии.

UnsupportedOperationException

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

ArithmeticException

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

SecurityException

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

NumberFormatException

NumberFormatException — это тип RuntimeException, который возникает, когда вызывается метод для преобразования строки в числовой формат, но строка не имеет соответствующего формата. Чтобы решить эту проблему, мы должны сначала проверить ввод пользователя, прежде чем пытаться его преобразовать. Также проверьте свой код, чтобы убедиться, что вы пытаетесь преобразовать строки, имеющие соответствующий формат для целевого числового типа.

InterruptedException

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

FileNotFoundException

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

IOException

IOException — это проверенное исключение в Java, представляющее ошибку, возникшую при выполнении операций ввода или вывода, таких как чтение или запись в файл или сетевой сокет. Она может произойти по нескольким причинам, например, из-за отсутствия или недоступности указанного файла, сетевой ошибки или недостаточных разрешений. Чтобы исправить проблему, нужно выполнить несколько действий, таких как проверка сообщения об ошибке, обработка исключения с помощью try-catch, закрытие ресурсов, проверка разрешения на доступ к файлу и так далее.

NoSuchMethodException

NoSuchMethodException — это исключение, которое возникает во время выполнения, когда мы пытаемся вызвать метод, не существующий в классе. Такое исключение обычно возникает, если мы вызываем метод с помощью Class.getMethod() или Class.getDeclaredMethod(), а указанное имя метода не найдено в классе или интерфейсе. Исключение также может возникнуть, когда мы пытаемся вызвать метод с помощью класса java.lang.reflect.Method, а указанное имя метода не существует в объекте. Чтобы избежать такого исключения, убедитесь, вызываете допустимый метод с правильной сигнатурой метода и спецификатором доступа.

NoSuchFieldException

NoSuchFieldException — это исключение времени выполнения, которое возникает, когда мы пытаемся получить доступ к полю, которого нет в классе. Это исключение обычно возникает, когда мы вызываем метод с помощью Class.getField() или Class.getDeclaredField(), а указанное имя поля не найдено в классе или интерфейсе. Кроме того, оно также может быть вызвано, если мы пытаемся получить доступ к полю с помощью класса java.lang.reflect.Field, а указанное имя поля не существует в объекте. Чтобы избежать этого исключения, убедитесь, что вы обращаетесь к действительному полю с правильным именем и модификаторами доступа. Если вы обращаетесь к приватному полю, обязательно используйте метод getDeclaredField() вместо метода getField().

IllegalAccessException

IllegalAccessException — это исключение времени выполнения, которое возникает, когда мы пытаемся получить доступ к полю или методу в классе, но у нас нет необходимых прав доступа. Это исключение обычно появляется, если мы пытаемся получить доступ к приватному полю или методу извне класса или когда мы пытаемся получить доступ к защищенному полю или методу из класса, который не является подклассом исходного класса. Также оно может быть вызвано при попытке получения доступа к полю или методу, который был помечен как недоступный с помощью класса java.lang.reflect.AccessibleObject. Чтобы избежать этой проблемы, убедитесь, что у вас есть необходимые права доступа к полю или методу, к которому вы пытаетесь получить доступ. Если поле или метод являются закрытыми, вам может потребоваться использовать отражение (reflection) и установить для AccessibleObject значение true (чтобы получить к нему доступ). Однако будьте осторожны при использовании отражения для доступа к закрытым полям или методам, так как это может нарушить инкапсуляцию и нарушить целостность класса.

VerifyError

VerifyError — это ошибка времени выполнения, которая является подклассом LinkageError. Она происходит, когда виртуальная машина Java (JVM) встречает файл класса, который нарушает определенные правила проверки. Когда класс Java компилируется, компилятор проверяет, соответствует ли байт-код определенным правилам и ограничениям, таким как безопасность типов и правильное использование стека и локальных переменных. Если файл класса нарушает эти правила, JVM выдаст VerifyError при загрузке и проверке класса во время выполнения. Чтобы избежать VerifyError, убедитесь, что ваш код соответствует правильному синтаксису и семантике языка Java. Если вы столкнулись с VerifyError, вы должны проверить свой код, чтобы убедиться, что он действителен и что отсутствуют нарушения правил проверки байт-кода Java.

OutOfMemoryError

OutOfMemoryError — это подкласс Error, тип Throwable, представляющий серьезные проблемы, которые невозможно устранить во время выполнения. Несмотря на то, что Java 8 включает в себя некоторые улучшения в сборке мусора и управлении памятью, вы по-прежнему можете столкнуться с OutOfMemoryError, если ваше приложение использует слишком много памяти или неправильно управляет использованием памяти. Чтобы избежать ошибки OutOfMemoryError, необходимо правильно управлять использованием памяти в приложении Java. Это предполагает использование структур данных и алгоритмов, которые эффективно используют память, избежание ненужного создания объектов и правильное удаление объектов, когда они больше не нужны. Кроме того, вы можете увеличить максимальный размер кучи для JVM, используя флаг -Xmx при запуске вашей Java-программы.

StackOverflowError

StackOverflowError — это тип ошибки, возникающий, когда размер стека, требуемый программой, превышает объем выделенной для нее памяти. Она может произойти, когда программа вызывает слишком много вложенных методов или когда метод слишком много раз рекурсивно вызывает сам себя, что приводит к бесконечному циклу. Виртуальная машина Java (JVM) выделяет фиксированный объем памяти для стека выполнения, который используется для отслеживания вызовов методов и локальных переменных. Когда стек переполняется, JVM выдает StackOverflowError. Чтобы избежать StackOverflowError, важно убедиться, что ваша Java-программа адекватно использует рекурсию и вызовы методов. Если вы столкнулись с ошибкой StackOverflowError, вы можете попробовать увеличить размер стека, используя флаг -Xss при запуске вашей Java-программы.

InvocationTargetException

InvocationTargetException — это проверенное исключение, которое генерируется механизмом отражения Java. Он входит в состав пакета java.lang.reflect.InvocationTargetException и используется для указания того, что во время вызова метода или конструктора возникло исключение. Когда метод или конструктор вызывается с использованием механизма отражения Java, вызывается метод invoke() класса java.lang.reflect.Method или java.lang.reflect.Constructor. Если вызываемый метод или конструктор выдает исключение, то метод invoke() перехватывает его и заключает в InvocationTargetException. Затем это исключение передается вызывающей стороне метода invoke(). Чтобы исправить InvocationTargetException, нам нужно перехватить его, получить исключение основной причины с помощью метода getCause() и соответствующим образом обработать исключение основной причины. Обратите внимание, что основной причиной может быть проверенное исключение или исключение времени выполнения, поэтому обязательно обработайте его правильно.

Способы исправления наиболее распространенных исключений в Java

Как исправить NullPointerException

Сценарий: у вас есть метод, который обращается к объекту, имеющему значение null.

String title= null;
System.out.println(title.length()); // Это вызовет NullPointerException
Решение № 1: Перед использованием проверьте, является ли объект нулевым.

if(title!= null) {
   System.out.println(title.length());
} else {
   System.out.println("title is null");
}
Решение № 2: Используйте Optional, чтобы избежать исключения NullPointerException.

Optional<String> optionalTitle = Optional.ofNullable(getTitle());
if (optionalTitle.isPresent()) { 
   String title= optionalTitle.get(); 
   System.out.println("Title: " + title);
} else { 
   System.out.println("Title is not available.");
}

Как исправить ArrayIndexOutOfBoundsException

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

int[] numbers = {4, 5, 6};
System.out.println(numbers[3]);   // Это вызовет ArrayIndexOutOfBoundsException
Решение: проверьте длину массива перед доступом к нему и убедитесь, что вы используете допустимые индексы.

int[] numbers = {4, 5, 6};
if (numbers.length > 3) {
   System.out.println(numbers[3]);
} else {
   System.out.println("ArrayIndexOutOfBoundsException: Please use valid indexes of the Array");
}

Как исправить ClassCastException

Сценарий: вы пытаетесь привести объект к типу, несовместимому с его фактическим типом.

Object obj = "Java Exception";
Integer number = (Integer) obj; // Это вызовет ClassCastException
Решение: Убедитесь, что вы приводите объекты только к тем типам, с которыми они совместимы.

Object obj = "Java Exception";
if(obj instanceof Integer) {
   Integer number = (Integer) obj;
   System.out.println(number);
} else {
   System.out.println("Object cannot caste to Integer");
}

Как исправить IllegalArgumentException

Сценарий: Вы передали недопустимый аргумент методу.

public void printNumber(int number) {
   if(number <= 0) {
      throw new IllegalArgumentException("You cannot pass a negative number or zero");
   }
   System.out.println(number);
}

printNumber(-1); // Это вызовет IllegalArgumentException
Решение: Убедитесь, что вы передаете допустимые аргументы методам. В этом случае передайте положительное число.

printNumber(1); //  Это успешно напечатает 1.

Как исправить IllegalStateException

Сценарий: объект находится в недопустимом состоянии.

public class Bike {

   private Boolean isStarted;

   public void start() {
      if(isStarted) {
        throw new IllegalStateException("Bike is already started");
      }
      isStarted = true;
      System.out.println("Bike started");
   }
}

Bike bike= new Bike();
bike.start();
bike.start(); // Это вызовет IllegalStateException потому что bike is already started
Решение: убедитесь, что вы правильно поддерживаете состояние объекта.

Bike bike= new Bike();
bike.start();

Как исправить UnsupportedOperationException

Сценарий: Вы используете операцию, которая не поддерживается объектом. Один из популярных примеров: когда вы используете операцию remove() для неизменяемой коллекции, то вы наверняка увидите исключение UnsupportedOperationException.

List<String> list = Arrays.asList("Java", "Angular", "Spring");
list.add("Python"); // Это вызовет UnsupportedOperationException
Поскольку метод Arrays.asList() возвращает неизменяемый список, он не поддерживает добавление или удаление элементов. Решение: Убедитесь, что вы вызываете только поддерживаемые операции над объектами.

List<String> list = new ArrayList<>(Arrays.asList("Java", "Angular" "Spring"));
list.add("Python");
System.out.println(list);

Как исправить ArithmeticException

Сценарий № 1: Вы пытаетесь выполнить операцию целочисленного деления, которая дает дробный результат.

int i = 10;
int j = 4;
int k = i/j; // Это вызовет исключение ArithmeticException: целочисленное деление будет дробным
Здесь результат операции деления равен 2,5, что является дробным значением. Поскольку целые числа не могут хранить дробные значения, возникает исключение ArithmeticException. Решение: Чтобы избежать этого исключения, мы можем использовать тип данных, поддерживающий дробные значения, например double, для хранения результата операции деления. Вот пример:

int i = 10;
int j = 4;
double k = (double) i/j;
Сценарий № 2: Вы пытаетесь разделить на ноль, и у вас возникает это исключение. Это наиболее распространенный сценарий.

int i = 4;
int j = 0;
int k = i/j; // Это вызовет исключение ArithmeticException: нельзя делить на ноль
Решение: Правильно обрабатывать деление на ноль. Например, приведенный ниже код демонстрирует правильную обработку.

int i = 4;
int j = 0;
if(j != 0) {
int k = i/j;
System.out.println(k);
} else {
System.out.println("ArithmeticException: Cannot divide by zero");
}

Как исправить IndexOutOfBoundsException

Сценарий: Вы пытаетесь получить доступ к коллекции с индексом, который находится за ее пределами.

List<String> list = Arrays.asList("Apple", "Papaya", "Mango");
System.out.println(list.get(3)); // Это вызовет IndexOutOfBoundsException
Решение: проверьте размер коллекции перед доступом к ней и убедитесь, что вы используете допустимые индексы.

List<String> list = Arrays.asList("Apple", "Papaya", "Mango");
if (list.size() > 3) {
   System.out.println(list.get(3));
} else {
   System.out.println("You are using the Index which is out of bounds");
}

Как исправить IOException

Сценарий: операция ввода-вывода (input/output) завершается сбоем, так как файл недоступен.

try {
   File inputFile = new FileReader("pqr.txt");
   BufferedReader reader = new BufferedReader(inputFile);
   String line = reader.readLine();
   System.out.println(line);
} catch (IOException e) {
   e.printStackTrace();
}
Решение: Обработайте ошибки ввода-вывода и убедитесь, что ресурсы правильно закрыты.

File inputFile = new File("pqr.txt");

if (!inputFile.exists() || !inputFile.canRead()) { 
 System.out.println("The input file is missing or not readable."); 
 return;
}

try {
BufferedReader reader = new BufferedReader(inputFile);
 String line = reader.readLine();
 System.out.println(line);
  reader.close();
} catch (IOException e) {
 e.printStackTrace();
}
Обратите внимание, что в качестве альтернативы мы можем использовать функцию try-with-resource, представленную в Java 7, для автоматического закрытия ресурсов, как показано ниже. Мы можем объявить один или несколько ресурсов в операторе try, и Java автоматически закроет ресурсы в конце блока, независимо от того, завершается ли блок нормально или возникает исключение.

try (BufferedReader reader = new BufferedReader(new FileReader("pqr.txt"))) {
....
} catch {
....
}

Как исправить FileNotFoundException

Сценарий: файл не найден в указанном месте.

try {
     BufferedReader reader = new BufferedReader(new FileReader("abc.txt"));
     String line = reader.readLine();
     System.out.println(line);
     reader.close();
} catch (FileNotFoundException | IOException e) {
     System.out.println("An error has occurred while reading the file: " + e.getMessage());
} 
Решение: Убедитесь, что файл существует и у вас есть соответствующие разрешения для доступа к нему.

try {
    File file = new File("abc.txt");
    if(!file.exists()) {
    throw new FileNotFoundException("File not found at the specified location");
    }
    BufferedReader reader = new BufferedReader(new FileReader(file));
    String line = reader.readLine();
    System.out.println(line);
    reader.close();
} catch (IOException e) {
    e.printStackTrace();
}

Как исправить NoSuchMethodException

Сценарий: если вы пытаетесь получить доступ к методу, который не может быть найден.

public class TestClass {
   public void sayHello() {
      System.out.println("Hello");
   }
}

TestClass obj = new TestClass();
Method method = obj.getClass().getMethod("printHello"); // Это вызовет NoSuchMethodException
Решение: Убедитесь, что метод существует, а также правильность имени и подписи метода.

public class TestClass {
   public void sayHello() {
      System.out.println("Hello");
   }
}

TestClass  obj = new TestClass();
try {
    Method method = obj.getClass().getMethod("sayHello");
} catch (NoSuchMethodException e) {
    e.printStackTrace();
}

Как исправить ConcurrentModificationException

Сценарий: коллекция изменяется во время ее повторения.

List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
for (String str : list) {
  list.remove(str);  // Это вызовет ConcurrentModificationException
}
Решение: Используйте итератор для перебора коллекции и ее изменения с помощью методов итератора.

List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String str = iterator.next();
    iterator.remove();
}
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
King Kazzzma 007 Уровень 45
18 апреля 2024
10/4 вызовет ArithmeticException...серьезно???