Исключения ·
‹ Предыдущий Следующий ›
⏱ 5 минут чтения Обновлено: 2026-10-30

Частые исключения в Java: причины и как исправить

Строка int apples = stock.get("apple"); выглядит безопасной: метод get() вызывается у существующей карты. Но программа падает с NullPointerException — ключа "apple" в карте нет, get() вернул null, а автораспаковка в int незаметно вызвала у него intValue().

import java.util.HashMap;
import java.util.Map;

public class StockDemo {
    public static void main(String[] args) {
        Map<String, Integer> stock = new HashMap<>();
        stock.put("pear", 5);
        int apples = stock.get("apple");
        System.out.println(apples);
    }
}
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because the return value of "java.util.Map.get(Object)" is null
    at StockDemo.main(StockDemo.java:8)

Частые исключения в Java — это несколько непроверяемых (unchecked) исключений, которые JVM и стандартная библиотека выбрасывают при ошибках в логике программы: NullPointerException (обращение к null), ArrayIndexOutOfBoundsException (индекс за пределами массива), ClassCastException (недопустимое приведение типа), NumberFormatException (строка не является числом), IllegalArgumentException и IllegalStateException (недопустимый аргумент или состояние объекта) и ConcurrentModificationException (изменение коллекции во время перебора). Почти всегда их исправляют не блоком catch, а правкой кода: проверкой на null, правильной границей цикла, instanceof перед приведением.

Сводная таблица частых исключений

Все исключения из этого урока — непроверяемые наследники RuntimeException: компилятор не требует их обрабатывать, и обнаруживаются они только во время выполнения. Все они, кроме ConcurrentModificationException из пакета java.util, лежат в пакете java.lang и доступны без import.

Исключение Пакет Тип Типичная причина Как исправить
NullPointerException java.lang unchecked Вызов метода, обращение к полю, элементу массива или автораспаковка у null Проверка на null, getOrDefault(), Objects.requireNonNullElse()
ArrayIndexOutOfBoundsException java.lang unchecked Индекс меньше 0 или не меньше длины массива, чаще всего i <= arr.length i < arr.length, цикл for-each, проверка args.length
StringIndexOutOfBoundsException, IndexOutOfBoundsException java.lang unchecked charAt()/substring() за пределами строки, list.get(i) при i >= size() Сверять индекс с length() или size()
ClassCastException java.lang unchecked Приведение объекта к типу, которым он на самом деле не является instanceof с pattern matching, generics без raw types
NumberFormatException java.lang unchecked Integer.parseInt() или Double.parseDouble() получил не число: "abc", " 42", "3,14" strip(), try-catch на границе ввода, Scanner.hasNextInt()
IllegalArgumentException java.lang unchecked Метод получил недопустимое значение аргумента Проверить или нормализовать значение до вызова
IllegalStateException java.lang unchecked Метод вызван не вовремя: поток Stream уже использован, Scanner закрыт Соблюдать порядок вызовов, создавать объект заново
ConcurrentModificationException java.util unchecked Удаление или добавление элементов коллекции внутри for-each removeIf(), Iterator.remove()
ArithmeticException java.lang unchecked Целочисленное деление на ноль: / by zero Проверить делитель перед делением
UnsupportedOperationException java.lang unchecked add() или remove() у неизменяемой коллекции, например List.of(...) Скопировать в изменяемую: new ArrayList<>(List.of(...))

Почему эти исключения непроверяемые и где они стоят в дереве классов, рассказано в уроке Иерархия исключений в Java.

Что ловить, а что исправлять

Перехватывать в catch оправданно обычно только NumberFormatException и IllegalArgumentException — там, где в программу приходят данные от пользователя, из файла или сети. NullPointerException, ArrayIndexOutOfBoundsException, ClassCastException и ConcurrentModificationException означают баг в коде: catch его только спрячет. Чем опасно «проглоченное» исключение — в разделе Типичные промахи новичков.

NullPointerException: обращение к null

NullPointerException (NPE) выбрасывается, когда программа обращается к объекту через ссылку, равную null: вызывает у неё метод, читает или записывает поле, берёт элемент или длину массива, распаковывает null в примитив. Начиная с Java 15 сообщение JVM прямо называет операцию и выражение, которое оказалось null.

Типичные причины NullPointerException

Сообщения в таблице — для кода, скомпилированного в IDE или сборщиком (Maven, Gradle), где имена переменных сохраняются; как выглядит сообщение без них, показано ниже.

Что делает код Пример Сообщение JVM (Java 15+)
Вызывает метод у null name.length() Cannot invoke "String.length()" because "name" is null
Читает поле user.name Cannot read field "name" because "user" is null
Записывает поле user.name = "Анна" Cannot assign field "name" because "user" is null
Берёт элемент массива arr[0] при int[] arr = null Cannot load from int array because "arr" is null
Берёт длину массива arr.length Cannot read the array length because "arr" is null
Распаковывает null в примитив int n = map.get("x"); Cannot invoke "java.lang.Integer.intValue()" because the return value of "java.util.Map.get(Object)" is null
Использует неинициализированное поле this.title.trim() Cannot invoke "String.trim()" because "this.title" is null

Ещё два источника NPE — throw ссылки, равной null (разобрано в разделе Что можно и нельзя бросать через throw), и synchronized по null.

Пример: helpful NullPointerException с javac и javac -g

public class NpeDemo {
    public static void main(String[] args) {
        String name = null;
        System.out.println(name.length());
    }
}

Компиляция и запуск из командной строки без дополнительных ключей:

javac NpeDemo.java
java NpeDemo
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "<local1>" is null
    at NpeDemo.main(NpeDemo.java:4)

Если скомпилировать класс с ключом -g (javac -g NpeDemo.java), вместо <local1> появится настоящее имя переменной:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "name" is null
    at NpeDemo.main(NpeDemo.java:4)

Откуда берётся <local1>

Имена локальных переменных хранятся в class-файле только в отладочной информации, которую javac записывает с ключом -g. Без неё JVM знает лишь номер слота: в статическом main слот 0 занимает args, поэтому первая объявленная переменная — <local1>. IntelliJ IDEA, Maven и Gradle по умолчанию компилируют с отладочной информацией, так что в IDE вы увидите имя переменной. Имена полей ("this.title") и методов в сообщении есть всегда.

Подробные сообщения NPE (helpful NullPointerExceptions, JEP 358) появились в Java 14 и включены по умолчанию начиная с Java 15. В Java 8 и 11 то же исключение выводится без текста — только java.lang.NullPointerException и номер строки.

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

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

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public class NpeFixDemo {
    public static void main(String[] args) {
        Map<String, Integer> stock = new HashMap<>();
        stock.put("pear", 5);

        int apples = stock.getOrDefault("apple", 0);
        System.out.println("Яблок: " + apples);

        String city = null;
        System.out.println("Москва".equals(city));
        System.out.println(Objects.requireNonNullElse(city, "не указан"));

        if (city != null) {
            System.out.println(city.length());
        }
    }
}
Яблок: 0
false
не указан
  • getOrDefault() вместо get(), если в карте может не быть ключа, — так исправляется пример из начала урока.
  • Константа слева: "Москва".equals(city) возвращает false для null, а city.equals("Москва") выбросит NPE.
  • Objects.requireNonNullElse(value, default) (Java 9+) подставляет значение по умолчанию.
  • Optional в качестве возвращаемого типа метода явно говорит вызывающему коду, что результата может не быть.
  • Objects.requireNonNull(arg, "arg") в начале метода выбрасывает NPE сразу при входе, с понятным сообщением, а не через десять вызовов — см. таблицу стандартных исключений в разделе Оператор throw и throw new.

ArrayIndexOutOfBoundsException и другие IndexOutOfBounds

ArrayIndexOutOfBoundsException выбрасывается при обращении к элементу массива по индексу меньше 0 или не меньше его длины: у массива из трёх элементов допустимы только индексы 0, 1 и 2. Сообщение в Java 11+ содержит и индекс, и длину: Index 3 out of bounds for length 3.

public class AioobeDemo {
    public static void main(String[] args) {
        int[] numbers = {10, 20, 30};
        for (int i = 0; i <= numbers.length; i++) {
            System.out.println(numbers[i]);
        }
    }
}
10
20
30
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
    at AioobeDemo.main(AioobeDemo.java:5)

Типичные причины:

  • <= вместо < в условии цикла, как в примере, или обращение arr[arr.length] вместо arr[arr.length - 1] к последнему элементу.
  • Нумерация с 1: цикл for (int i = 1; i <= n; i++), перенесённый из математической записи.
  • Отрицательный индекс после вычислений: Index -1 out of bounds for length 3.
  • Пустой массив args: args[0] при запуске программы без аргументов даёт Index 0 out of bounds for length 0.

StringIndexOutOfBoundsException и IndexOutOfBoundsException

ArrayIndexOutOfBoundsException и StringIndexOutOfBoundsException — подклассы IndexOutOfBoundsException, поэтому catch (IndexOutOfBoundsException e) перехватит все три.

Исключение Где возникает Пример Сообщение
ArrayIndexOutOfBoundsException Обращение к элементу массива arr[3] при int[] arr = new int[3] Index 3 out of bounds for length 3
StringIndexOutOfBoundsException Методы String: charAt(), substring() "Java".charAt(10) Содержит индекс и длину строки; точный текст отличается в разных версиях JDK
IndexOutOfBoundsException Методы списков: get(), set(), remove(int) new ArrayList<String>().get(0) Index 0 out of bounds for length 0
import java.util.ArrayList;
import java.util.List;

public class ListIndexDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        System.out.println(list.size());
        System.out.println(list.get(0));
    }
}
0
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0
    ...
    at ListIndexDemo.main(ListIndexDemo.java:8)

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

Сравнивайте индекс с длиной строго через <, а если индекс в теле цикла не нужен — используйте for-each, в котором выйти за границы невозможно. Внешние данные (аргументы, ввод пользователя) проверяйте перед обращением по индексу.

public class AioobeFixDemo {
    public static void main(String[] args) {
        int[] numbers = {10, 20, 30};
        for (int n : numbers) {
            System.out.println(n);
        }
        String first = args.length > 0 ? args[0] : "нет аргументов";
        System.out.println(first);
    }
}

Вывод при запуске без аргументов:

10
20
30
нет аргументов

ClassCastException: недопустимое приведение типа

ClassCastException выбрасывается, когда объект приводят к классу или интерфейсу, которым он на самом деле не является. Компилятор пропускает такое приведение, если статический тип переменной (Object, суперкласс, интерфейс) в принципе может содержать нужный тип, а реальный объект проверяется только во время выполнения.

public class ClassCastDemo {
    public static void main(String[] args) {
        Object value = 42;
        String text = (String) value;
        System.out.println(text);
    }
}
Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')
    at ClassCastDemo.main(ClassCastDemo.java:4)

Сообщение читается так: реальный класс объекта — java.lang.Integer, а приводили его к java.lang.String; в скобках указано, из какого модуля и загрузчика взяты оба класса. Для ваших собственных классов там будет are in unnamed module of loader 'app'. Приведение заведомо несовместимых типов, например (String) 42, вообще не скомпилируется: incompatible types: int cannot be converted to String.

ClassCastException без явного приведения

Что выведет этот код? В нём нет ни одного оператора приведения, но компилятор вставляет его сам при чтении из generic-коллекции.

import java.util.ArrayList;
import java.util.List;

public class HiddenCastDemo {
    public static void main(String[] args) {
        List raw = new ArrayList();
        raw.add(42);
        List<String> names = raw;
        System.out.println("Размер: " + names.size());
        String first = names.get(0);
    }
}
Размер: 1
Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')
    at HiddenCastDemo.main(HiddenCastDemo.java:10)

Число попало в List<String> через «сырой» тип List без параметра. Компилятор предупредил об этом (uses unchecked or unsafe operations), но код собрался, а исключение возникло гораздо позже — в строке 10, при чтении элемента. Не используйте raw types и не игнорируйте предупреждения unchecked.

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

Перед приведением проверяйте тип оператором instanceof (подробнее — в уроке «Приведение ссылочных типов в Java»). С Java 16 проверка и приведение записываются одной конструкцией — pattern matching: if (a instanceof Dog dog).

public class AnimalCastDemo {
    static class Animal { }

    static class Dog extends Animal {
        void bark() {
            System.out.println("Гав!");
        }
    }

    static class Cat extends Animal { }

    public static void main(String[] args) {
        Animal[] animals = {new Dog(), new Cat()};
        for (Animal a : animals) {
            if (a instanceof Dog dog) {
                dog.bark();
            } else {
                System.out.println("Не собака: " + a.getClass().getSimpleName());
            }
        }
    }
}
Гав!
Не собака: Cat

Без проверки приведение (Dog) a для кошки закончилось бы сообщением class AnimalCastDemo$Cat cannot be cast to class AnimalCastDemo$Dog. Если же приходится проверять тип в длинной цепочке if-else, это повод заменить её полиморфным методом в базовом классе.

NumberFormatException: For input string

NumberFormatException выбрасывают методы Integer.parseInt(), Long.parseLong(), Double.parseDouble() и valueOf(), когда строка не является корректной записью числа. Сообщение For input string: "..." показывает исходную строку в кавычках — по ним видно даже лишние пробелы. Это подкласс IllegalArgumentException.

public class NfeDemo {
    public static void main(String[] args) {
        String input = "abc";
        int number = Integer.parseInt(input);
        System.out.println(number);
    }
}
Exception in thread "main" java.lang.NumberFormatException: For input string: "abc"
    ...
    at NfeDemo.main(NfeDemo.java:4)

Чаще всего исключение вызывают строки, которые человеку кажутся числом:

Вызов Результат Почему
Integer.parseInt("42"), Integer.parseInt("+7") 42, 7 Корректная запись, знак + допустим
Integer.parseInt(" 42") For input string: " 42" Пробелы не отбрасываются автоматически
Integer.parseInt("3.14") For input string: "3.14" Дробное число для int
Double.parseDouble("3,14") For input string: "3,14" parseDouble понимает только точку, независимо от локали
Integer.parseInt("3000000000") For input string: "3000000000" Больше Integer.MAX_VALUE (2147483647); нужен Long.parseLong()
Integer.parseInt("") For input string: "" Пустая строка, например пустое поле формы
Integer.parseInt(null) NumberFormatException, а не NPE Текст сообщения для null отличается в разных версиях JDK

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

Ввод пользователя нельзя заранее сделать корректным, поэтому здесь try-catch уместен — но в одном месте, на границе ввода, с понятным результатом вместо «магического» нуля. Пробелы по краям убирает strip() (Java 11+), а при чтении из консоли можно заранее спросить scanner.hasNextInt().

import java.util.OptionalInt;

public class SafeParseDemo {
    static OptionalInt parseInt(String s) {
        if (s == null) {
            return OptionalInt.empty();
        }
        try {
            return OptionalInt.of(Integer.parseInt(s.strip()));
        } catch (NumberFormatException e) {
            return OptionalInt.empty();
        }
    }

    public static void main(String[] args) {
        String[] inputs = {"42", " 7 ", "abc", "3.14", null};
        for (String s : inputs) {
            System.out.println(s + " -> " + parseInt(s));
        }
    }
}
42 -> OptionalInt[42]
 7  -> OptionalInt[7]
abc -> OptionalInt.empty
3.14 -> OptionalInt.empty
null -> OptionalInt.empty

Вызывающий код сам решает, что делать с пустым результатом: попросить ввести число ещё раз, подставить значение по умолчанию или сообщить об ошибке.

IllegalArgumentException: недопустимый аргумент

IllegalArgumentException означает, что метод получил значение аргумента, с которым не может работать: отрицательный размер, неизвестное имя константы, число вне диапазона. Его выбрасывают методы JDK, и его же принято выбрасывать в собственном коде при проверке параметров. Текст сообщения задаёт автор метода.

Вызов JDK Сообщение
new ArrayList<>(-1) Illegal Capacity: -1
"ab".repeat(-1) count is negative: -1
DayOfWeek.valueOf("monday") No enum constant java.time.DayOfWeek.monday
Integer.parseInt("abc") For input string: "abc" (подкласс NumberFormatException)

Самый частый случай на практике — valueOf() у перечисления: имя константы сравнивается с учётом регистра и пробелов.

import java.time.DayOfWeek;

public class IaeDemo {
    public static void main(String[] args) {
        String input = "monday";
        DayOfWeek day = DayOfWeek.valueOf(input);
        System.out.println(day);
    }
}
Exception in thread "main" java.lang.IllegalArgumentException: No enum constant java.time.DayOfWeek.monday
    ...
    at IaeDemo.main(IaeDemo.java:6)

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

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

import java.time.DayOfWeek;
import java.util.Locale;

public class IaeFixDemo {
    public static void main(String[] args) {
        String input = " monday ";
        DayOfWeek day = DayOfWeek.valueOf(input.strip().toUpperCase(Locale.ROOT));
        System.out.println(day);
    }
}
MONDAY

Locale.ROOT в toUpperCase() защищает от сюрпризов локали: в турецкой локали строчная i превращается в İ с точкой, и "friday" перестало бы совпадать с FRIDAY. Как выбрасывать IllegalArgumentException в своём методе, показано в разделе Оператор throw и throw new.

IllegalStateException: метод вызван не вовремя

IllegalStateException выбрасывается, когда аргументы метода в порядке, но объект находится в состоянии, в котором этот метод вызывать нельзя: поток Stream уже использован, Scanner закрыт, итератор ещё не вернул элемент. Ошибка не в данных, а в порядке вызовов.

import java.util.stream.Stream;

public class StreamReuseDemo {
    public static void main(String[] args) {
        Stream<String> names = Stream.of("Анна", "Борис");
        names.forEach(System.out::println);
        System.out.println(names.count());
    }
}
Анна
Борис
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
    ...
    at StreamReuseDemo.main(StreamReuseDemo.java:7)

Другие методы JDK, которые выбрасывают это исключение:

Ситуация Сообщение
Повторная терминальная операция над тем же Stream stream has already been operated upon or closed
scanner.nextLine() после scanner.close() Scanner closed
matcher.group() до успешного matcher.find() или matches() No match found
iterator.remove() до next() или дважды подряд Без сообщения

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

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

import java.util.List;

public class StreamFixDemo {
    public static void main(String[] args) {
        List<String> names = List.of("Анна", "Борис");
        names.forEach(System.out::println);
        System.out.println(names.stream().count());
    }
}
Анна
Борис
2

Чем IllegalStateException отличается от IllegalArgumentException при выборе исключения для своего кода, разобрано в разделе Когда создавать свое исключение, а когда взять стандартное.

ConcurrentModificationException: изменение коллекции при обходе

ConcurrentModificationException (пакет java.util) выбрасывает итератор ArrayList, HashMap и других стандартных коллекций, если коллекцию изменили в обход итератора, пока он её перебирал. Самый частый случай — вызов list.remove() или list.add() внутри цикла for-each по тому же списку.

import java.util.ArrayList;
import java.util.List;

public class CmeDemo {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>(List.of("Анна", "Борис", "Вера"));
        for (String name : names) {
            if (name.startsWith("А")) {
                names.remove(name);
            }
        }
        System.out.println(names);
    }
}
Exception in thread "main" java.util.ConcurrentModificationException
    at java.base/java.util.ArrayList$Itr.checkForComodification(...)
    at java.base/java.util.ArrayList$Itr.next(...)
    at CmeDemo.main(CmeDemo.java:7)

Исключение возникает не в строке с remove(), а в строке 7 — заголовке цикла: for-each вызывает iterator.next(), а тот замечает, что счётчик изменений списка (modCount) уже не совпадает с ожидаемым. Сообщения у этого исключения нет.

Что выведет этот код?

Тот же цикл, но удаляем не первый элемент, а «Борис»:

for (String name : names) {
    if (name.startsWith("Б")) {
        names.remove(name);
    }
}
System.out.println(names);
[Анна, Вера]

Исключения нет, хотя код такой же неправильный. После удаления предпоследнего элемента размер списка стал 2, курсор итератора тоже равен 2, и hasNext() возвращает false — цикл завершается, не дойдя до проверки в next(). «Вера» при этом так и не была проверена условием. Итераторы стандартных коллекций работают по принципу fail-fast «по возможности»: отсутствие исключения не доказывает, что код корректен.

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

Удаляйте элементы методом removeIf() (Java 8+) или через remove() самого итератора — оба способа обновляют состояние итератора вместе со списком.

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class CmeFixDemo {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>(List.of("Анна", "Борис", "Вера"));
        names.removeIf(name -> name.startsWith("А"));
        System.out.println(names);

        Iterator<String> it = names.iterator();
        while (it.hasNext()) {
            if (it.next().startsWith("В")) {
                it.remove();
            }
        }
        System.out.println(names);
    }
}
[Борис, Вера]
[Борис]

Не только про потоки

Слово «Concurrent» в названии сбивает с толку: все примеры выше однопоточные. Если же коллекцию действительно изменяют и читают несколько потоков, обычный ArrayList или HashMap не подходит вовсе — берите CopyOnWriteArrayList или ConcurrentHashMap из java.util.concurrent: их итераторы ConcurrentModificationException не выбрасывают.

Как читать сообщение и не допускать этих исключений

Сообщение об исключении состоит из трёх частей: полное имя класса исключения, текст после двоеточия и стек вызовов. Имя класса говорит о категории ошибки, текст — о конкретном значении (индексе, строке, выражении, равном null), а первая строка at с вашим классом указывает файл и номер строки. Как устроен стек вызовов, разобрано в уроке Обработка исключений в Java.

Фрагмент сообщения Что искать в коде
because "x" is null, because the return value of "...get(Object)" is null Откуда в x или в результат метода пришёл null
Index 3 out of bounds for length 3 Индекс равен длине: условие <= или лишний + 1
class A cannot be cast to class B Где объект класса A попал в переменную, которую считают B
For input string: " 42" Кавычки показывают пробелы, запятые и пустую строку во вводе
ConcurrentModificationException без текста add()/remove() у коллекции внутри её же for-each

Чтобы эти исключения не появлялись вовсе:

  • Проверяйте аргументы в начале метода (Objects.requireNonNull(), throw new IllegalArgumentException(...)) — ошибка проявится там, где появились плохие данные, а не в глубине вызовов.
  • Предпочитайте конструкции, в которых ошибиться нельзя: for-each вместо индексного цикла, removeIf() вместо удаления в цикле, pattern matching instanceof вместо голого приведения, getOrDefault() вместо get().
  • Не перехватывайте исключения-баги: catch (NullPointerException e) или catch (Exception e) «на всякий случай» прячет причину. Как правильно строить try-catch для ожидаемых ошибок — в уроке Оператор try-catch-finally в Java.
  • Не игнорируйте предупреждения компилятора и IDE: unchecked, «Possible NullPointerException», «Array index is out of bounds» часто указывают на будущее исключение ещё до запуска.

Часто задаваемые вопросы

java.lang.NullPointerException: как исправить за три шага?

Первое — найдите в стеке вызовов первую строку at с вашим классом: там файл и номер строки. Второе — прочитайте в сообщении, какое выражение оказалось null (в Java 15+ оно названо после слова because). Третье — выясните, должно ли значение там быть: если да, исправьте место, где ссылку не инициализировали; если оно законно может отсутствовать, добавьте проверку или значение по умолчанию. Приёмы с примерами — в разделе Как исправить NullPointerException.

Как получить подробное сообщение NullPointerException в Java 8 или 11?

Никак: helpful NullPointerExceptions появились только в Java 14. В Java 14 их включает флаг -XX:+ShowCodeDetailsInExceptionMessages, с Java 15 они работают по умолчанию (отключить можно флагом -XX:-ShowCodeDetailsInExceptionMessages). На Java 8 и 11 остаётся номер строки из стека вызовов — поэтому полезно не писать длинные цепочки вида a.getB().getC().getD() в одной строке.

Почему catch (IllegalArgumentException e) перехватывает NumberFormatException?

Потому что NumberFormatException — подкласс IllegalArgumentException, а catch перехватывает указанный класс и всех его наследников. По той же причине catch (IndexOutOfBoundsException e) поймает и ArrayIndexOutOfBoundsException, и StringIndexOutOfBoundsException. Если нужны оба блока, catch подкласса должен стоять первым, иначе код не скомпилируется.

Почему List.of(...).add() выбрасывает UnsupportedOperationException?

List.of(), Set.of(), Map.of() (Java 9+), Collections.unmodifiableList() и stream().toList() (Java 16+) возвращают неизменяемые коллекции: любая попытка add(), remove() или set() выбрасывает UnsupportedOperationException без сообщения. Arrays.asList() разрешает set(), но не add() и remove(). Если список нужно менять, скопируйте его: new ArrayList<>(List.of(...)).

Как удалить элементы из HashMap во время перебора без ConcurrentModificationException?

Используйте removeIf() у представлений карты: map.entrySet().removeIf(e -> e.getValue() == 0), map.keySet().removeIf(...) или map.values().removeIf(...). Другой способ — явный Iterator по entrySet() и вызов iterator.remove(). Вызов map.remove(key) внутри for-each по keySet() приводит к тому же исключению, что и со списком.

Чем StackOverflowError и OutOfMemoryError отличаются от этих исключений?

Это не исключения, а ошибки: наследники Error, а не Exception. Они сигнализируют о проблеме уровня JVM (переполнен стек вызовов, закончилась память), их не перехватывает catch (Exception e), и обычно их не обрабатывают в коде. Разбор с примером — в разделе catch (Exception e) и StackOverflowError.

Комментарии

Зарегистрируйтесь или войдите, чтобы иметь возможность оставить комментарий.