Написать обработчик ошибки создания отрицательного размера java

Глава 9

Обработка исключений

Основные навыки и понятия

  • Представление об иерархии исключений
  • Использование ключевых слов try и catch
  • Последствия неперехвата исключений
  • Применение нескольких операторов catch
  • Перехват исключений, генерируемых подклассами
  • Вложенные блоки try
  • Генерирование исключений
  • Представление о членах класса Throwable
  • Использование ключевого слова finally
  • Использование ключевого слова throws
  • Представление о исключениях, встроенные в Java
  • Создание специальных классов исключений

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

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

В Java определены стандартные исключения для наиболее часто встречающихся программных ошибок, в том числе деления на нуль или попытки открыть несуществующий файл. Для того чтобы обеспечить требуемую реакцию на конкретную ошибку, в программу следует включить соответствующий обработчик событий. Исключения широко применяются в библиотеке Java API.

Иерархия исключений

В Java все исключения представлены отдельными классами. Все классы исключений являются потомками класса Throwable. Так, если в программе возникнет исключительная ситуация, будет сгенерирован объект класса, соответствующего определенному типу исключения. У класса Throwable имеются два непосредственных подкласса: Exception и Error. Исключения типа Error относятся к ошибкам, возникающим в виртуальной машине Java, а не в прикладной программе. Контролировать такие исключения невозможно, поэтому реакция на них в прикладной программе, как правило, не предусматривается. В связи с этим исключения данного типа не будут описываться в этой книге.

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

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

Для обработки исключений в Java предусмотрены пять ключевых слов: try, catch, throw, throws и finally. Они образуют единую подсистему, в которой использование одного ключевого слова почти всегда автоматически влечет за собой употребление другого. Каждое из упомянутых выше ключевых слов будет подробно рассмотрено далее в этой главе. Но прежде следует дать общее представление об их роли в процессе обработки исключений. Поэтому ниже поясняется вкратце, каким образом они действуют.

Операторы, в которых требуется отслеживать появление исключений, помещаются в блок try. Если в блоке try будет сгенерировано исключение, его можно перехватить и обработать нужным образом. Системные исключения генерируются автоматически. А для того чтобы сгенерировать исключение вручную, следует воспользоваться ключевым словом throw. Иногда возникает потребность обрабатывать исключения за пределами метода, в котором они возникают, и для этой цели служит ключевое слово throws. Если же некоторый фрагмент кода должен быть выполнен обязательно и независимо от того, возникнет исключение или нет, его следует поместить в блок finally.

Использование ключевых слов try и catch

Основными языковыми средствами обработки исключений являются ключевые слова try и catch. Они используются совместно. Это означает, что нельзя указать ключевое слово catch в коде, не указав ключевое слово try. Ниже приведена общая форма записи блоков try/catch, предназначенных для обработки исключений.

try {
    // Блок кода, в котором должны отслеживаться ошибки
}
catch (тип_исключения_1 объект_исключения) {
    // Обработчик исключения тип_исключения_1
}
catch (тип_исключения_2 объект_исключения) {
    // Обработчик исключения тип_исключения_2
}

В скобках, следующих за ключевым словом catch, указываются тип исключения и переменная, ссылающаяся на объект данного типа. Когда возникает исключение, оно перехватывается соответствующим оператором catch, обрабатывающим это исключение. Как следует из приведенной выше общей формы записи, с одним блоком try может быть связано несколько операторов catch. Тип исключения определяет, какой именно оператор catch будет выполняться. Так, если тип исключения соответствует типу оператора catch, то именно он и будет выполнен, а остальные операторы catch — пропущены. При перехвате исключения переменной, указанной в скобках после ключевого слова catch, присваивается ссылка на объект_исключения.

Следует иметь в виду, что если исключение не генерируется, блок try завершается обычным образом и ни один из его операторов catch не выполняется. Выполнение программы продолжается с первого оператора, следующего за последним оператором catch. Таким образом, операторы catch выполняются только при появлении исключения.

На заметку.
В версии JDK 7 внедрена новая форма оператора try, поддерживающая автоматическое управления ресурсами и называемая оператором try с ресурсами. Более подробно она описывается в главе 10 при рассмотрении потоков ввода-вывода, в том числе и тех, что связаны с файлами, поскольку потоки ввода-вывода относятся к числу ресурсов, наиболее употребительных в прикладных программах.

Простой пример обработки исключений

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

// Демонстрация обработки исключений,
class ExcDemol {
    public static void main (String args[]) {
        int nums[] = new int[4];

        // Создание блока try.
        try {
            System.out.println("Before exception is generated.");

            // Попытка обратиться за границы массива.
            nums[7] = 10;
            System.out.println("this won't be displayed");
        }
        // Перехват исключения в связи с обращением за границы массива.
        catch (ArraylndexOutOfBoundsException exc) {
            System.out.println("Index out-of-bounds!");
        }
        System.out.println("After catch statement.");
    }
}

Результат выполнения данной программы выглядит следующим образом:

Before exception is generated.
Index out-of-bounds!
After catch statement.

Несмотря на всю простоту данного примера программы, он наглядно демонстрирует несколько важных особенностей обработки исключений. Во-первых, код, подлежащий проверке на наличие ошибок, помещается в блок try. И во-вторых, когда возникает исключение (в данном случае это происходит при попытке обратиться за границы массива), выполнение блока try прерывается и управление получает блок catch. Следовательно, явного обращения к блоку catch не происходит, но переход к нему осуществляется лишь при определенном условии, возникающем в ходе выполнения программы. Так, оператор вызова метода println(), следующий за выражением, в котором происходит обращение к несуществующему элементу массива, вообще не выполняется. По завершении блока catch выполнение программы продолжается с оператора, следующего за этим блоком. Таким образом, обработчик исключений предназначен для устранения программных ошибок, приводящих к исключительным ситуациям, а также для обеспечения нормального продолжения исполняемой программы.

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

на следующую строку кода:

Теперь исключение не возникнет и блок catch не выполнится.

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

/* Исключение может быть сгенерировано одним методом,
   а перехвачено другим. */

class ExcTest {
    // сгенерировать исключение
    static void genException()  {
        int nums[] = new int[4];

        System.out.println("Before exception is generated.");

        // Здесь генерируется исключение в связи с
        // обращением за границы массива.
        nums[7] = 10;
        System.out.println("this won't be displayed");
    }
}

class ExcDemo2 {
    public static void main(String args[])  {
        try {
            ExcTest.genException() ;
        }
        //А здесь исключение перехватывается.
        catch (ArraylndexOutOfBoundsException exc) {
            System.out.println("Index out-of-bounds!");
        }
        System.out.println("After catch statement.");
    }
}

Выполнение этой версии программы дает такой же результат, как и при выполнении ее предыдущей версии. Этот результат приведен ниже.

Before exception is generated.
Index out-of-bounds!
After catch statement.

Метод genException() вызывается из блока try, и поэтому генерируемое, но не перехватываемое в нем исключение перехватывается далее в блоке catch в методе main(). Если бы метод genException() сам перехватывал исключение, оно вообще не дошло бы до метода main().

Последствия неперехвата исключений

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

// Обработка ошибки средствами виртуальной машины Java,
class NotHandled {
    public static void main(String args[]) {
        int nums[] = new int[4];

        System.out.println("Before exception is generated.");

        // Попытка обращения за границы массива,
        nums[7] = 10;
    }
}

При появлении ошибки, связанной с обращением за границы массива, выполнение программы прекращается и выводится следующее сообщение:

Exception in thread "main" java.lang.ArraylndexOutOfBoundsException: 7 at NotHandled.main(NotHandled.java:9)

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

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

// Эта программа не будет работать нормально!
class ExcTypeMismatch {
    public static void main(String args[]) {
        int nums[] = new int[4];
        try {
        System.out.println("Before exception is generated.");
        // При выполнении следующего оператора возникает
        // исключение ArraylndexOutOfBoundsException
        nums[7] = 10;
        System.out.println("this won’t be displayed");
        }
        /* Исключение, связанное с обращением за границы массива,
        нельзя обработать с помощью оператора catch, в котором
        указан тип исключения ArithmeticException. */
        catch (ArithmeticException exc) {
        System.out.println("Index out-of-bounds!");
        }
        System.out.println("After catch statement.");
    }
}

Ниже приведен результат выполнения данной программы.

Before exception is generated.
Exception in thread "main" java.lang.ArraylndexOutOfBoundsException: 7
    at ExcTypeMismatch.main(ExcTypeMismatch.java:10)

Нетрудно заметить, что оператор catch, в котором указан тип исключения ArithmeticException, не может перехватить исключение ArraylndexOutOfBoundsException.

Обработка исключений — изящный способ устранения программных ошибок

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

// Изящная обработка исключения и продолжение выполнения программы,
class ExcDemo3 {
    public static void main(String args[])  {
        int numer[] = { 4, 8, 16, 32, 64, 128 };
        int denom[] = { 2, 0, 4, 4, 0, 8 };

        for(int i=0; i<numer.length; i++)   {
            try {
                System.out.println(numer[i] + " / " +
                                   denom[i] + " is " +
                                   numer[i]/denom[i]);
            }
            catch (ArithmeticException exc) {
                // перехватить исключение
                System.out.println("Can't divide by Zero!");
            }
        }
    }
}

Результат выполнения данной программы выглядит следующим образом:

4 / 2 is 2
Can't divide by Zero!
16/ 4 is 4
32 / 4 is 8
Can't divide by Zero!
128 / 8 is 16

Данный пример демонстрирует еще одну важную особенность: обработанное исключение удаляется из системы. Иными словами, на каждом шаге цикла блок try выполняется в программе сызнова, а все возникшие ранее исключения считаются обработанными. Благодаря этому в программе могут обрабатываться повторяющиеся ошибки.

Применение нескольких операторов catch

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

// Применение нескольких операторов catch.  '
class ExcDemo4 {
    public static void main(String args[]) {
        // Здесь массив numer длиннее массива denom.
        int numer[] = { 4, 8, 16, 32, 64, 128, 256, 512 };
        int denom[] = { 2, 0, 4, 4, 0, 8 };

        for(int i=0; i<numer.length; i++)   {
            try {
                System.out.println(numer[i] + " / " +
                                   denom[i] + " is " +
                                   numer[i]/denom[i]);
            }
            // За блоком try следует несколько блоков catch подряд,
            catch (ArithmeticException exc) {
                // перехватить исключение
                System.out.println("Can't divide by Zero!");
            }
            catch (ArraylndexOutOfBoundsException exc) {
                // перехватить исключение
                System.out.println("No matching element found.");
            }
        }
    }
}

Выполнение этой программы дает следующий результат:

4 / 2 is 2
Can't divide by Zero!
16 / 4 is 4
32 / 4 is 8
Can't divide by Zero!
128 / 8 is 16
No matching element found.
No matching element found.

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

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

Перехват исключений, генерируемых подклассами

В отношении подклассов следует отметить еще одну интересную особенность применения нескольких операторов catch: условие перехвата исключений для суперкласса будет справедливо и для любых его подклассов. Например, класс Throwable является суперклассом для всех исключений, поэтому для перехвата всех возможных исключений в операторах catch следует указывать тип Throwable. Если же требуется перехватывать исключения типа суперкласса и типа подкласса, то в блоке операторов первым должен быть указан тип исключения, генерируемого подклассом. В противном случае вместе с исключением типа суперкласса будут перехвачены и все исключения производных от него классов. Это правило соблюдается автоматически, и если указать первым тип исключения, генерируемого суперклассом, то будет создан недостижимый код, поскольку условие перехвата исключения, генерируемого подклассом, никогда не будет выполнено. А ведь недостижимый код в Java считается ошибкой.

Рассмотрим в качестве примера следующую программу

//В операторах catch исключения типа подкласса должны
// предшествовать исключениям типа суперкласса,
class ExcDemo5 {
    public static void main(String args[]) {
        // Здесь массив numer длиннее массива denom.
        int numer[] = { 4, 8, 16, 32, 64, 128, 256, 512 };
        int denom[] = { 2, 0, 4, 4, 0, 8 };

        for(int i=0; i<numer.length; i++)   {
            try {
                System.out.println(numer[i] + " / " +
                                   denom[i] + " is " +
                                   numer[i]/denom[i]);
            }
            // Перехват исключения от подкласса.
            catch (ArraylndexOutOfBoundsException exc) {
                System.out.println("No matching element found.");
            }
            // Перехват исключения от суперкласса.
            catch (Throwable exc) {
                System.out.println("Some exception occurred.");
            }
        }
    }
}

Ниже приведен результат выполнения данной программы.

4 / 2 is 2
Some exception occurred.
16 / 4 is 4
32 / 4 is 8
Some exception occurred.
128 / 8 is 16
No matching element found.
No matching element found.

В данном случае оператор catch (Throwable) перехватывает все исключения, кроме ArraylndexOutOfBoundsException. Соблюдение правильного порядка следования операторов catch приобретает особое значение в том случае, когда исключения генерируются в самой программе.

Вложенные блоки try

Блоки try могут быть вложенными друг в друга. Исключение, возникшее во внутреннем блоке try и не перехваченное связанным с ним блоком catch, распростра¬няется далее во внешний блок try и обрабатывается связанным с ним блоком catch. Такой порядок обработки исключений демонстрируется в приведенном ниже примере программы, где исключение ArraylndexOutOfBoundsException не перехватывается во внутреннем блоке catch, но обрабатывается во внешнем.

// Применение вложенных блоков try.
class NestTrys {
    public static void main(String args[]) {
        // Массив numer длиннее, чем массив denom.
        int numer[] = { 4, 8, 16, 32, 64, 128, 256, 512 };
        int denom[] = { 2, 0, 4, 4, 0, 8 };

        // Вложенные блоки try.
        try { // Внешний блок try.
            for(int i=0; i<numer.length; i++)   {
                try { // Внутренний блок try.
                    System.out.println(numer[i] + " / " +
                                       denom[i] + " is " +
                                       numer[i]/denom[i]) ;
                }
                catch (ArithmeticException exc) {
                    // перехватить исключение
                    System.out.println("Can't divide by Zero!");
                }
            }
        }
        catch (ArraylndexOutOfBoundsException exc) {
            // перехватить исключение
            System.out.println("No matching element found.");
            System.out.println("Fatal error - program terminated.");
        }
    }
}

Выполнение этой программы может дать, например, следующий результат:

4 / 2 is 2
Can't divide by Zero!
16 / 4 is 4
32 / 4 is 8
Can't divide by Zero!
128 / 8 is 16
No matching element found.
Fatal error - program terminated.

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

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

Генерирование исключений

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

где объект_исключения должен быть объектом класса, производного от класса Throwable.

Ниже приведен пример программы, демонстрирующий применение оператора throw. В этой программе исключение ArithmeticException генерируется вручную.

// Генерирование исключения вручную,
class ThrowDemo {
    public static void main(String args[])  {
        try {
            System.out.println("Before throw.");
                // Генерирование исключения.
                throw new ArithmeticException() ;
        }
        catch (ArithmeticException exc) {
            // перехватить исключение
            System.out.println("Exception caught.");
        }

        System.out.println("After try/catch block.");
    }
}

Выполнение этой программы дает следующий результат:

Before throw.
Exception caught.
After try/catch block.

Обратите внимание на то, что исключение ArithmeticException генерируется с помощью ключевого слова new в операторе throw. Дело в том, что оператор throw генерирует исключение в виде объекта. Поэтому после ключевого слова throw недостаточно указать только тип исключения, нужно еще создать объект для этой цели.

Повторное генерирование исключений

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

Ниже приведен пример программы, демонстрирующий повторное генерирование исключений.

//•Повторное генерирование исключений,
class Rethrow {
    public static void genException()   {
        // Массив numer длиннее маесивв denom.
        int numer[] = { 4, 8, 16, 32, 64, 128, 256, 512 };
        int denom[] = { 2, 0, 4, 4, 0, 8 };

        for(int i=0; i<numer.length; i++)   {
            try {
                System.out.println(numer[i] + " / " +
                                   denom[i] + " is " +
                                   numer[i]/denom[i]);
            }
            catch (ArithmeticException exc) {
                // перехватить исключение
                System.out.println("Can11 divide by Zero!");
            }
            catch (ArraylndexOutOfBoundsException exc) {
                // перехватить исключение
                System.out.println("No matching element found.");
                throw exc; // Повторное генерирование исключения.
            }
        }
    }
}

class RethrowDemo {
    public static void main(String args[])  {
        try {
            Rethrow.genException();
        }
        catch(ArraylndexOutOfBoundsException exc) {
            // Перехват повторно сгенерированного включения.
            System.out.println("Fatal error - " +
                               "program terminated.");
        }
    }
}

В данной программе ошибка деления на нуль обрабатывается локально в методе genException(), а при попытке обращения за границы массива исключение генерируется повторно. На этот раз оно перехватывается в методе main().

Подробнее о классе Throwable

В приведенных до сих примерах программ только перехватывались исключения, но не выполнялось никаких действий над представляющими их объектами. В выражении оператора catch указываются тип исключения и параметр, принимающий объект исключения. А поскольку все исключения представлены подклассами, производными от класса Throwable, то они поддерживают методы, определенные в этом классе. Некоторые наиболее употребительные методы из класса Throwable приведены в табл. 9.1.

Таблица 9.1. Наиболее употребительные методы из класса Throwable

Метод Описание
Throwable filllnStackTrace() Возвращает объект типа Throwable, содержащий полную трассировку стека исключений. Этот объект пригоден для повторного генерирования исключений
String getLocalizedMessage() Возвращает описание исключения, локализованное по региональным стандартам
String getMessage() Возвращает описание исключения
void printStackTrace() Выводит трассировку стека исключений
void printStackTrace(PrintStream stream) Выводит трассировку стека исключений в указанный поток
void printStackTrace(PrintWriter stream) Направляет трассировку стека исключений в указанный поток
String toString() Возвращает объект типа String, содержащий полное описание исключения. Этот метод вызывается из метода println() при выводе объекта типа Throwable

Среди методов, определенных в классе Throwable, наибольший интерес представляют методы pr intStackTrace() и toString(). С помощью метода printStackTrace() можно вывести стандартное сообщение об ошибке и запись последовательности вызовов методов, которые привели к возникновению исключения, А метод toString() позволяет получить стандартное сообщение об ошибке. Этот метод также вызывается в том случае, когда объект исключения передается в качестве параметра методу println(). Применение этих методов демонстрируется в следующем примере программы:

// Применение методов из класса Throwable.
class ExcTest {
    static void genException()  {
        int nums[] = new int[4];

        System.out.println("Before exception is generated.");

        // сгенерировать исключение в связи с попыткой
        // обращения за границы массива
        nums[7] = 10;
        System.out.println("this won't be displayed");
    }
}

class UseThrowableMethods {
    public static void main(String args[])  {

        try {
            ExcTest.genException() ;
        }
        catch (ArraylndexOutOfBoundsException exc) {
            // перехватить исключение
            System.out.println("Standard message is: ");
            System.out.println(exc) ;
            System.out.println("nStack trace: ");
            exc.printStackTrace();
        }

        System.out.println("After catch statement.");
    }
}

Результат выполнения данной программы выглядит следующим образом:

Before exception is generated.
Standard message is:
java.lang.ArraylndexOutOfBoundsException: 7

Stack trace:
java.lang.ArraylndexOutOfBoundsException: 7
    at ExcTest.genException(UseThrowableMethods.java:10)
    at UseThrowableMethods.main(UseThrowableMethods.java:19)
After catch statement.

Использование ключевого слова finally

Иногда требуется определить кодовый блок, который должен выполняться по завершении блока try/catch. Допустим, в процессе работы программы возникло исключение, требующее ее преждевременного завершения. Но в программе открыт файл или установлено сетевое соединение, а следовательно, файл нужно закрыть, а соединение разорвать. Для выполнения подобных операций нормального завершения программы удобно воспользоваться ключевым словом finally.

Для того чтобы определить код, который должен выполняться по завершении блока try/catch, нужно указать блок finally в конце последовательности операторов try/catch. Ниже приведена общая форма записи блока try/catch вместе с блоком finally.

try {
    // Блок кода, в котором отслеживаются ошибки.
}
catch (тип_исключения_1 объект_исключения) {
    // Обработчик исключения тип_исключения_1
}
catch (тип_исключения_2 объект_исключения) {
    // Обработчик исключения тип_исключения_2
}
//. . .
finally {
// Код блока finally
}

Блок finally выполняется всегда по завершении блока try/catch независимо от того, какое именно условие к этому привело. Следовательно, блок finally получит управление как при нормальной работе программы, так и при возникновении ошибки. Более того, он будет вызван даже в том случае, если в блоке try или в одном из блоков catch будет присутствовать оператор return для немедленного возврата из метода.

Ниже приведен краткий пример программы, демонстрирующий применение блока finally.

// Применение блока finally,
class UseFinally {
    public static void genException(int what) {
        int t;
        int nums[] = new int[2];

        System.out.println("Receiving " + what);
        try {
            switch(what) {
            case 0:
                t = 10 / what; // сгенерировать ошибку деления на нуль
                break;
            case 1:
                nums[4] = 4; // сгенерировать ошибку обращения к массиву
                break;
            case 2:
                return; // возвратиться из блока try
            }
        }
        catch (ArithmeticException exc) {
            // перехватить исключение
            System.out.println("Can1t divide by Zero!");
            return; // возвратиться из блока catch
        }
        catch (ArraylndexOutOfBoundsException exc) {
            // перехватить исключение
            System.out.println("No matching element found.");
        }
        // Этот блок выполняется независимо от того, каким
        // образом завершается блок try/catch.
        finally {
            System.out.println("Leaving try.");
        }
    }
}

class FinallyDemo {
    public static void main(String args[]) {

        for(int i=0; i < 3; i++) {
            UseFinally.genException(i);
            System.out.println() ;
        }
    }
}

В результате выполнения данной программы получается следующий результат:

Receiving О
Can't divide by Zero!
Leaving try.

Receiving 1
No matching element found.
Leaving try.

Receiving 2
Leaving try.

Нетрудно заметить, что блок finally выполняется независимо от того, каким об¬
разом завершается блок try/catch.

Использование ключевого слова throws

Иногда исключения нецелесообразно обрабатывать в том методе, в котором они возникают. В таком случае их следует указывать с помощью ключевого слова throws. Ниже приведена общая форма объявления метода, в котором присутствует ключевое слово throws.

возвращаемый_тип имя_метода(список_параметров) throws список_исключений {
    // Тело метода
}

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

Возможно, вам покажется странным, что в ряде предыдущих примеров ключевое слово throws не указывалось при генерировании исключений за пределами методов. Дело в том, что исключения, генерируемые подклассом Error или RuntimeException, можно и не указывать в списке оператора throws. Исполняющая система Java по умолчанию предполагает, что метод может их генерировать. А исключения всех остальных типов следует непременно объявить с помощью ключевого слова throws. Если этого не сделать, возникнет ошибка при компиляции.

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

throws java.io.IOException

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

Рассмотрим пример, в котором осуществляется обработка исключения IOException. В методе prompt() отображается сообщение, а затем выполняется ввод символов с клавиатуры. Такой ввод данных может привести к возникновению исключения IOException. Но это исключение не обрабатывается в методе prompt(). Вместо этого в объявлении метода указан оператор throws, т.е. обязанности по обработке данного исключению поручаются вызывающему методу. В данном случае вызывающим является метод main(), в котором и перехватывается исключение.

// Применение ключевого слова throws,
class ThrowsDemo {
    // Обратите внимание на оператор throws в объявлении метода.
    public static char prompt(String str)
        throws java.io.IOException {

        System.out.print(str + ": ");
        return (char) System.in.read() ;
    }

    public static void main(String args[]) {
        char ch;

        try {
            // В методе prompt() может быть сгенерировано исключение,
            // поэтому данный метод следует вызывать в блоке try.
            ch = prompt("Enter a letter");
        }
        catch(java.io.IOException exc) {
            System.out.println("I/O exception occurred.");
            ch = 'X';
        }
        System.out.println("You pressed " + ch);
    }
}

Обратите внимание на одну особенность приведенного выше примера. Класс IOException относится к пакету java. io. Как будет разъяснено в главе 10, в этом пакете содержатся многие языковые средства Java для организации ввода-вывода. Следовательно, пакет java.io можно импортировать, а в программе указать только имя класса IOException.

Новые средства обработки исключений, внедренные в версии JDK 7

С появлением версии JDK 7 механизм обработки исключений в Java был значительно усовершенствован благодаря внедрению трех новых средств. Первое из них поддерживает автоматическое управление ресурсами, позволяющее автоматизировать процесс освобождения таких ресурсов, как файлы, когда они больше не нужны. В основу этого средства положена расширенная форма оператора try, называемая оператором try с ресурсами и описываемая в главе 10 при рассмотрении файлов. Второе новое средство называется многократным перехватом, а третье — окончательным или более точным повторным генерированием исключений. Два последних средства рассматриваются ниже.

Многократный перехват позволяет перехватывать два или более исключения одним оператором catch. Как пояснялось ранее, после оператора try можно (и даже принято) указывать два или более оператора catch. И хотя каждый блок оператора catch, как правило, содержит свою особую кодовую последовательность, нередко в двух или более блоках оператора catch выполняется одна и та же кодовая последовательность, несмотря на то, что в них перехватываются разные исключения. Вместо того чтобы перехватывать каждый тип исключения в отдельности, теперь можно воспользоваться единым блоком оператора catch для обработки исключений, не дублируя код.

Для организации многократного перехвата следует указать список исключений в одном операторе catch, разделив их типы оператором поразрядного ИЛИ. Каждый параметр многократного перехвата неявно указывается как final. (По желанию модификатор доступа final можно указать и явным образом, но это совсем не обязательно.) А поскольку каждый параметр многократного перехвата неявно указывается как final, то ему нельзя присвоить новое значение.

В приведенной ниже строке кода показывается, каким образом многократный перехват исключений ArithmeticException и ArraylndexOutOfBoundsException указывается в одном операторе catch.

catch(final ArithmeticException | ArraylndexOutOfBoundsException e) {

Ниже приведен краткий пример программы, демонстрирующий применение многократного перехвата исключений.

// Применение средства многократного перехвата исключений.
// Примечание: для компиляции этого кода требуется JDK 7
// или более поздняя версия данного комплекта,
class MultiCatch {
    public static void main(String args[]) {
        int a=88, b=0;
        int result;
        char chrs[] = { 'А', 'В', 'C' };

        for(int i=0; i < 2; i++)    {
            try {
                if (i == 0)
                    // сгенерировать исключение ArithmeticException
                    result = а / b;
                else
                    // сгенерировать исключение ArraylndexOutOfBoundsException
                    chrs[5] = 'X';
            }
            // В этом операторе catch организуется перехват обоих исключений,
            catch(ArithmeticException | ArraylndexOutOfBoundsException е) {
                System.out.println("Exception caught: " + e);
            }
        }

        System.out.println("After multi-catch.");
    }
}

В данном примере программы исключение ArithmeticException генерируется при попытке деления на нуль, а исключение ArraylndexOutOfBoundsException — при попытке обращения за границы массива chrs. Оба исключения перехватываются одним оператором catch.

Средство более точного повторного генерирования исключений ограничивает этот процесс лишь теми проверяемыми типами исключений, которые генерируются в соответствующем блоке try и не обрабатываются в предыдущем блоке оператора catch, а также относятся к подтипу или супертипу указываемого параметра. И хотя такая возможность требуется нечасто, ничто не мешает теперь воспользоваться ею в полной мере. А для организации окончательного повторного генерирования исключений параметр оператора catch должен быть, по существу, указан как final. Это означает, что ему нельзя присвоить новое значение в блоке catch. Он может быть указан как final явным образом, хотя это и не обязательно.

В стандартном пакете java. lang определены некоторые классы, представляющие стандартные исключения Java. Часть из них использовалась в предыдущих примерах программ. Наиболее часто встречаются исключения из подклассов стандартного класса RuntimeException. А поскольку пакет java. lang импортируется по умолчанию во все программы на Java, то исключения, производные от класса RuntimeException, становятся доступными автоматически. Их даже обязательно включать в список оператора throws. В терминологии языка Java такие исключения называют непроверяемыми, поскольку компилятор не проверяет, обрабатываются или генерируются подобные исключения в методе. Непроверяемые исключения, определенные в пакете java.lang, приведены в табл. 9.2, тогда как в табл. 9.3 — те исключения из пакета j ava. lang, которые следует непременно включать в список оператора throws при объявлении метода, если, конечно, в методе содержатся операторы, способные генерировать эти исключения, а их обработка не предусмотрена в теле метода. Такие исключения принято называть проверяемыми. В Java предусмотрен также ряд других исключений, определения которых содержатся в различных библиотеках классов. К их числу можно отнести упоминавшееся ранее исключение IOException.

Таблица 9.2. Непроверяемые исключения, определенные в пакете java.lang

Исключение Описание
ArithmeticException Арифметическая ошибка, например попытка деления на нуль
ArraylndexOutOfBoundsException Попытка обращения за границы массива
ArrayStoreException Попытка ввести в массив элемент, несовместимый с ним по типу
ClassCastException Недопустимое приведение типов
EnumConstNotPresentException Попытка использования нумерованного значения, которое не было определено ранее
IllegalArgumentException Недопустимый параметр при вызове метода
IllegalMonitorStateException Недопустимая операция контроля, например, ожидание разблокировки потока
IllegalStateException Недопустимое состояние среды выполнения или приложения
IllegalThreadStateException Запрашиваемая операция несовместима с текущим состоянием потока
IndexOutOfBoundsException Недопустимое значение индекса
NegativeArraySizeException Создание массива отрицательного размера
NullPointerException Недопустимое использование пустой ссылки
NumberFormatException Неверное преобразование символьной строки в число
SecurityException Попытка нарушить систему защиты
StringlndexOutOfBounds Попытка обращения к символьной строке за ее границами
TypeNotPresentException Неизвестный тип
UnsupportedOperationException Неподдерживаемая операция

Таблица 9.3. Проверяемые исключения, определенные в пакете java.lang

Исключение Описание
ClassNotFoundException Класс не найден
CloneNotSupportedException Попытка клонирования объекта, не реализующего интерфейс Cloneable
IllegalAccessException Доступ к классу запрещен
InstantiationException Попытка создания объекта абстрактного класса или интер¬фейса
InterruptedException Прерывание одного потока другим
NoSuchFieldException Требуемое поле не существует
NoSuchMethodException Требуемый метод не существует
ReflectiveOperationException Суперкласс исключений, связанных с рефлексией (добавлен в версии JDK 7)

Создание подклассов, производных от класса Exception

Несмотря на то что встроенные в Java исключения позволяют обрабатывать большинство ошибок, механизм обработки исключений не ограничивается только этими ошибками. В частности, можно создавать исключения для обработки потенциальных ошибок в прикладной программе. Создать исключение несложно. Для этого достаточно определить подкласс, производный от класса Exception, который, в свою очередь, является подклассом, порожденным классом Throwable. В создаваемый подкласс не обязательно включать реализацию каких-то методов. Сам факт существования такого подкласса позволяет использовать его в качестве исключения.

В классе Exception не определены новые методы. Он лишь наследует методы, предоставляемые классом Throwable. Таким образом, все исключения, включая и создаваемые вами, содержат методы класса Throwable. Конечно же, вы вольны переопределить в создаваемом вами классе один или несколько методов.

Ниже приведен пример, в котором создается исключение NonlntResultException. Оно генерируется в том случае, если результатом деления двух целых чисел является дробное число. В классе NonlntResultException содержатся два поля, предназначенные для хранения целых чисел, а также конструктор. В нем также переопределен метод toString(), что дает возможность выводить описание исключения с помощью метода println().

// Применение специально создаваемого исключения.
// создать исключение
class NonlntResultException extends Exception {
    int n;
    int d;

    NonlntResultException(int i, int j) {
        n = i;
        d = j;
    }

    public String toString()    {
        return "Result of " + n + " / " + d +
                " is non-integer.";
    }
}

class CustomExceptDemo {
    public static void main(String args[]) {

        // В массиве numer содержатся нечетные числа,
        int numer[] = { 4, 8, 15, 32, 64, 127, 256, 512 };
        int denom[] = { 2, 0, 4, 4, 0, 8 };

        for(int i=0; i<numer.length; i++)   {
            try {
                if((numer[i]%2) != 0)
                    throw new
                        NonlntResultException(numer[i], denom[i]);
                System.out.println(numer[i] + " / " +
                                   denom[i] + 11 is " +
                                   numer[i]/denom[i]);
            }
            catch (ArithmeticException exc) {
                // перехватить исключение
                System.out.println("Can11 divide by Zero!");
            }
            catch (ArraylndexOutOfBoundsException exc) {
                // перехватить исключение
                System.out.println("No matching element found.");
            }
            catch (NonlntResultException exc) {
                System.out.println(exc) ;
            }
        }
    }
}

Результат выполнения данной программы выглядит следующим образом:

4 / 2 is 2
Can't divide by Zero!
Result of 15 / 4 is non-integer.
32 / 4 is 8
Can't divide by Zero!
Result of 127 / 8 is non-integer.
No matching element found.
No matching element found.

Пример для опробования 9.1.
Добавление исключений в класс очереди

В этом проекте предстоит создать два класса исключении, которые будут использоваться классом очереди, разработанным в примере для опробования 8.1. Эти исключения должны указывать на переполнение и опустошение очереди, а генерировать их будут методы put() и get() соответственно. Ради простоты эти исключения добавляются в класс FixedQueue, но вы можете без труда внедрить их в любые другие классы очереди, разработанные в примере для опробования 8.1.

Последовательность действий

  1. Создайте файл QExcDemo.java.
  2. Определите следующие исключения в файле QExcDemo.java:
    /*
    Пример для опробования 9.1.
    Добавление обработчиков исключений в класс очереди.
    */
    // Исключение, указывающее на переполнение очереди,
    class QueueFullException extends Exception {
        int size;
    
        QueueFullException(int s) { size = s; }
    
        public String toString()    {
            return "nQueue is full. Maximum size is " + size;
        }
    }
    
    // Исключение, указывающее на опустошение очереди,
    class QueueEmptyException extends Exception {
        public String toString()    {
            return "nQueue is empty.";
        }
    }
    

    Исключение QueueFullException генерируется при попытке поместить элемент в уже заполненную очередь, а исключение QueueEmptyException — в ответ на попытку извлечь элемент из пустой очереди.

  3. Измените класс FixedQueue таким образом, чтобы при возникновении ошибки он генерировал исключение. Соответствующий код приведен ниже. Введите этот код в файл QExcDemo.java.
    // Класс, реализующий очередь фиксированного размера
    // для хранения символов.
    class FixedQueue implements ICharQ {
        private char q[]; // Массив для хранения элементов очереди,
        private int putloc, getloc; // Индексы размещения и извлечения
    
        // элементов очереди.
        // создать пустую очередь заданного размера
        public FixedQueue(int size) {
            q = new char[size+1]; // выделить память для очереди
            putloc = getloc = 0;
        }
    
        // поместить символ в очередь
        public void put(char ch)
        throws QueueFullException {
    
            if(putloc==q.length-1)
                throw new QueueFullException(q.length-1);
    
            putloc++;
            q[putloc] = ch;
        }
    
        // извлечь символ из очереди
        public char get()
        throws QueueEmptyException {
    
            if(getloc == putloc)
                throw new QueueEmptyException();
    
            getloc++;
            return q[getloc];
        }
    }
    

    Добавление исключений в класс FixedQueue выполняется в два этапа. Сначала в определении методов get() и put() указывается оператор throws с типом генерируемого исключения. А затем в этих методах организуется генерирование исключений при возникновении ошибок. Используя исключения, можно организовать обработку ошибок в вызывающей части программы наиболее рациональным способом. Как вы помните, в предыдущих версиях рассматриваемой здесь программы выводились только сообщения об ошибках. А генерирование исключений является более профессиональным подходом к разработке данной программы.

  4. Для опробования усовершенствованного класса FixedQueue введите в файл QExcDemo.java приведенный ниже исходный код класса QExcDemo.
    // Демонстрация исключений при обращении с очередью,
    class QExcDemo {
        public static void main(String args[])  {
            FixedQueue q = new FixedQueue(10);
            char ch;
            int i;
    
            try {
                // Переполнение очереди.
                for(i=0; i < 11; i++)   {
                    System.out.print("Attempting to store : " +
                                     (char) ('A' + i));
                    q.put((char) (fA' + i));
                    System.out.println(" - OK");
                }
                System.out.println();
            }
            catch (QueueFullException exc) {
                System.out.println(exc);
            }
            System.out.println();
    
            try {
                // Попытка извлечь символ из пустой очереди.
                for(i=0; i < 11; i++) {
                System.out.print("Getting next char: ");
                ch = q.get();
                System.out.println(ch);
                }
            }
            catch (QueueEmptyException exc) {
                System.out.println(exc);
            }
        }
    }
    
  5. Класс FixedQueue реализует интерфейс ICharQ, в котором определены методы get() и put(), и поэтому интерфейс ICharQ необходимо изменить таким образом, чтобы в нем отражалось наличие операторов throws. Ниже приведен видоизмененный соответственно код интерфейса ICharQ. Не забывайте о том, что он должен храниться в файле ICharQjava.
    // Интерфейс очереди для хранения символов с генерированием исключений,
    public interface ICharQ {
        // поместить символ в очередь
        void put(char ch) throws QueueFullException;
        // извлечь символ из очереди
        char get() throws QueueEmptyException;
    }
    
  6. Скомпилируйте сначала новую версию исходного файла IQChar. j ava, а затем исходный файл QExcDemo. java и запустите программу QExcDemo на выполнение. В итоге вы получите следующий результат ее выполнения:
    Attempting to store A - OK
    Attempting to store В - OK
    Attempting to store С - OK
    Attempting to store D - OK
    Attempting to store E - OK
    Attempting to store F - OK
    Attempting to store G - OK
    Attempting to store H - OK
    Attempting to store I - OK
    Attempting to store J - OK
    Attempting to store К
    Queue is full. Maximum size is 10
    
    Getting next char: A
    Getting next char: В
    Getting next char: С
    Getting next char: D
    Getting next char: E
    Getting next char: F
    Getting next char: G
    Getting next char: H
    Getting next char: I
    Getting next char: J
    Getting next char:
    Queue is empty.
    

Упражнение для самопроверки по материалу главы 9

  1. Какой класс находится на вершине иерархии исключений?
  2. Объясните вкратце, как пользоваться ключевыми словами try и catch?
  3. Какая ошибка допущена в приведенном ниже фрагменте кода?
    // ...
    vals[18] = 10;
    catch (ArraylndexOutOfBoundsException exc) {
        // обработать ошибку
    }
    
  4. Что произойдет, если исключение не будет перехвачено?
  5. Какая ошибка допущена в приведенном ниже фрагменте кода?
    class A extends Exception { ...
    class В extends А { ...
        // ...
    try {
        // ...
    }
    catch (A exc) { ... }
    catch (В exc) { ... }
    
  6. Может ли внутренний блок catch повторно генерировать исключение, которое будет обработано во внешнем блоке catch?
  7. Блок finally — последний фрагмент кода, выполняемый перед завершением программы. Верно или неверно? Обоснуйте свой ответ.
  8. Исключения какого типа необходимо явно объявлять с помощью оператора throws, включаемого в объявление метода?
  9. Какая ошибка допущена в приведенном ниже фрагменте кода?
    class MyClass { // ... }
    // ...
    throw new MyClass();
    
  10. Отвечая на вопрос 3 упражнения для самопроверки по материалу главы 6, вы создали класс Stack. Добавьте в него специальные исключения для реагирования на попытку поместить элемент в переполненный стек и извлечь элемент из пустого стека.
  11. Какими тремя способами можно сгенерировать исключение?
  12. Назовите два подкласса, производных непосредственно от класса Throwable.
  13. Что такое многократный перехват?
  14. Следует ли перехватывать в программе исключения типа Error?

The NegativeArraySizeException is a runtime exception in Java that occurs when an application attempts to create an array with a negative size.

Since the NegativeArraySizeException is an unchecked exception, it does not need to be declared in the throws clause of a method or constructor.

What Causes NegativeArraySizeException in Java

The NegativeArraySizeException occurs when an attempt is made to assign a negative size to an array. Here’s an example:

public class NegativeArraySizeExceptionExample {
    public static void main(String[] args) {
        int[] array = new int[-5];
        System.out.println("Array length: " + array.length);
    }
}

Running the above code throws the following exception:

Exception in thread "main" java.lang.NegativeArraySizeException: -5
    at NegativeArraySizeExceptionExample.main(NegativeArraySizeExceptionExample.java:3)

How to Handle NegativeArraySizeException in Java

The NegativeArraySizeException can be handled in code using the following steps:

  • Surround the piece of code that can throw an NegativeArraySizeException in a try-catch block.
  • Catch the NegativeArraySizeException in the catch clause.
  • Take further action as necessary for handling the exception and making sure the program execution does not stop.

Here’s an example of how to handle it in code:

public class NegativeArraySizeExceptionExample {
    public static void main(String[] args) {
        try {
            int[] array = new int[-5];
        } catch (NegativeArraySizeException nase) {
            nase.printStackTrace();
            //handle the exception
        }
        System.out.println("Continuing execution...");
    }
}

In the above example, the lines that throw the NegativeArraySizeException are placed within a try-catch block. The NegativeArraySizeException is caught in the catch clause and its stack trace is printed to the console. Any code that comes after the try-catch block continues its execution normally.

Running the above code produces the following output:

java.lang.NegativeArraySizeException: -5
    at NegativeArraySizeExceptionExample.main(NegativeArraySizeExceptionExample.java:4)
Continuing execution...

How to Avoid NegativeArraySizeException in Java

Since the NegativeArraySizeException occurs when an array is created with a negative size, assigning a positive size to the array can help avoid the exception. Applying this to the earlier example helps fix the issue:

public class NegativeArraySizeExceptionExample {
    public static void main(String[] args) {
        int[] array = new int[5];
        System.out.println("Array length: " + array.length);
    }
}

The array is initialized with a size of 5, which is a positive number. Running the above code produces the correct output as expected:

Array length: 5

Track, Analyze and Manage Errors With Rollbar

Rollbar in action

Managing errors and exceptions in your code is challenging. It can make deploying production code an unnerving experience. Being able to track, analyze, and manage errors in real-time can help you to proceed with more confidence. Rollbar automates error monitoring and triaging, making fixing Java errors easier than ever. Sign Up Today!

Зміст

  • Что такое исключение в Java (Java Exception)?
  • Операторы try, catch, finally, throw, throws: обработка исключений и примеры использования
  • Иерархия исключений Java
  • Заключение

Что такое исключение в Java (Java Exception)?

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

Помогаем

Unrecognizable

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

Приведем пример кода, в котором возникает исключение.

class ExceptionTest{ 
    // Метод принимает два целых числа
    // и возвращает результат деления
    // первого на второе
    static float divide(int x, int y){
        float result = x / y;
        return result;
    }

    public static void main(String args[]){
        // Эта строка будет выполнена
        System.out.println(divide(4, 2));
        // Эта тоже
        System.out.println(divide(0, 2));
        // Эта выбросит исключение
        System.out.println(divide(4, 0));
    }
}

Если его запустить, получим следующий вывод:

Курс

БІЗНЕС-АНАЛІТИК В IT

Опануйте професію бізнес-аналітика в ІТ та заробляйте від $700 на початку кар’єри.

РЕЄСТРУЙТЕСЯ!

Professional consultancy

2.0
0.0
Exception in thread "main" java.lang.ArithmeticException: / by zero
        at ExceptionTest.divide(ExceptionTest.java:6)
        at ExceptionTest.main(ExceptionTest.java:16)

Выводятся два результата деления и сообщение об исключении. В этом сообщении указана следующая информация:

  • тип исключения (ArithmeticException);
  • какое именно исключение возникло (деление на ноль);
  • стек выполнения: методы и строки, в которых возникло исключение.

Операторы try, catch, finally, throw, throws: обработка исключений и примеры использования

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

try-catch

Для перехвата исключения используется конструкция trycatch. Код, который нужно проверить на исключение, заключается в блок try, а код, обрабатывающий исключение – в блок catch.

Добавим эти блоки в приведенный выше блок кода и дополним его парой переменных для наглядности.

class ExceptionTest{  
    // Метод принимает два целых числа
    // и возвращает результат деления
    // первого на второе
    static float divide(int x, int y){
        float result = x / y;
        return result;
    }

    public static void main(String args[]){
        int x = 4;
        int y = 2;
        try {
            // Эта строка будет выполнена
            System.out.println(divide(x, y));

            x = 0;
            // Эта тоже
            System.out.println(divide(x, y));

            x = 4;
            y = 0;
            // Эта выбросит исключение
            System.out.println(divide(x, y));
        } catch (ArithmeticException e) {
            System.out.println("Ошибка при делении " + x + " на " + y);
        }
    }
}

Получим более удобочитаемый вывод:

2.0
0.0
Ошибка при делении 4 на 0

finally

Блок finally выполняется после try-catch, независимо от того, возникло ли исключение. Это необязательный блок, но если нет блока catch, то блок finally необходим.

В этом блоке можно, например, закрыть файл, открытый в блоке try, как в приведенном ниже коде.

import java.io.FileWriter;
import java.io.IOException;

public class FinallyTest {
    public static void main(String[] args) {
        FileWriter writer = null;
        try {
            writer = new FileWriter("out.txt");
            writer.write("Writing to the file!");
            System.out.println("Файл записан успешно.");
        } catch (IOException e) {
            System.out.println("Ошибка записи в файл.");
            e.printStackTrace();
        } finally {
            if ( writer != null ){
                try{
                  writer.close();
                } catch (IOException e) {
                  System. out.println("Ошибка закрытия файла.");
                  e.printStackTrace();
                }
            }
        }
    }
}

throw

В некоторых случаях требуется выбросить исключение самостоятельно. Это делается при помощи ключевого слова throw.

В данном примере метод PrintMe выбрасывает исключение, если его аргумент равен null.

import java.util.LinkedList;

public class ThrowTest
{
  public static void main(String[] args) {
        LinkedList<String> fruits = new LinkedList<String>();
        fruits.add("apple");
        fruits.add("banana");
        fruits.add("orange");
        fruits.add("mango");

        // Печатает список
        ThrowTest.PrintMe(fruits);

        // Выбрасывает исключение
        ThrowTest.PrintMe(null);
  }

    public static void PrintMe(LinkedList<String> fruits){
        if (fruits == null){
            throw new NullPointerException("Аргумент не инициализирован");
        }
        System.out.println(fruits);
    }
}

throws

Ключевое слово throws используется, чтобы в сигнатуре метода указать, что он выбрасывает исключение. Его можно использовать, чтобы передавать исключения по стеку вызовов и указать, что эти исключения не обязательно должны обрабатываться в методе, в котором они объявлены.

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ThrowsTest{
    public static void readFromFile() throws IOException {
        // Указываем несуществующий файл, чтобы проверить работу исключения
        try (BufferedReader reader = new BufferedReader(new FileReader("out.txt"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line + "n");
            }
        }
    }

    public static void main(String[] args) {
    try {
            readFromFile();
        } catch (IOException ioe) {
            System.out.println("Файл не найден");
        }
    }
}

Иерархия исключений Java

Исключения в Java делятся на две основные категории: встроенные и пользовательские.

Встроенные исключения

Встроенные исключения – это исключения, которые заранее определены в Java.

Когда возникает встроенное исключение, виртуальная машина Java (JVM) создает объект, принадлежащий классу встроенного исключения. Все исключения происходят от класса java.lang.Throwable, но они определены в нескольких пакетах.

Класс Throwable происходит непосредственно об класса Object и является корневым классом дерева классов исключений. От него происходят два подкласса: Error и Exception. Ошибки и исключения, которые встречаются в программах на Java, являются объектами этих классов.

С использованием класса Throwable можно создавать собственные исключения.

Класс Error является надклассом для всех классов ошибок времени выполнения. Он завершает выполнение программы, если происходит ошибка, связанная с системой или ресурсами (JVM).

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

Примеры ошибок: AssertionError, LinkageError, OutOfMmeoryError, StackOverFlowError, VirtualMachineError.

Класс Exception представляет ошибки, вызванные программой или внешними факторами. Это надкласс для все классов исключений.

Для этого класса существует два конструктора:

  • public Exception() (по умолчанию)
  • public Exception(String message) (принимает строковое сообщение как аргумент)

Эти конструкторы наследуются всеми подклассами исключений. Сам по себе класс Exception не предоставляет собственных методов. Он наследует методы класса Throwable.

Встроенные исключения делятся на две группы: проверяемые (checked) и непроверяемые (unchecked).

Проверяемые исключения в Java

Проверяемые исключения проверяются компилятором Java во время компиляции и не являются подклассами RuntimeException (исключения времени выполнения).

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

Проверяемые исключения обрабатываются либо в блоке try-catch, либо в объявлении метода с ключевым словом throws. Если исключение не обработано, происходит ошибка компиляции.

Проверяемыми исключениями являются все исключения, кроме RuntimeException, Error и их подклассов.

Примеры проверяемых исключений: ClassNotFoundException, IOException, SQLException, IllegalAccessException, FileNotFoundException.

Непроверяемые исключения (исключения времени выполнения) в Java

Непроверяемые исключения в Java – это исключения, которые проверяются JVM, а не компилятором Java. Они возникают во время выполнения программы.

Все подклассы RuntimeException называются непроверяемыми исключениями или исключениями времени выполнения в Java.

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

Компилятор Java не проверяет исключения времени выполнения во время компиляции, независимо от того, обрабатывает ли их программа.

Если в методе возникает исключение времени выполнения, а программист не обрабатывает его, JVM прекращает выполнение программы, не выполняя остаток кода.

Примеры непроверяемых исключений: ArithmeticException, ArrayIndexOutOfBoundsException, ClassCastException, NegativeArraySizeException, NullPointerException.

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

Исключение Описание
ArithmeticException Выбрасывается, когда возникает исключительная арифметическая ситуация.
ArrayIndexOutOfBoundsException Выбрасывается при попытке обратиться к массиву по недействительному индексу.
ArrayStoreException Выбрасывается при попытке сохранить объект несоответствующего типа в массиве объектов.
ClassCastException Выбрасывается, когда код совершает попытку привести тип объекта к подклассу, экземпляром которого он не является.
ClassNotFoundException Выбрасывается, когда приложение совершает попытку загрузить класс по его имени в строковом представлении с использованием метода forName в классе Class.
CloneNotSupportedException Выбрасывается, когда для клонирования объекта определенного класса вызван метод clone, но класс этого объекта не реализует интерфейс Cloneable.
EnumConstantNotPresentException Выбрасывается, когда приложение производит попытку обратиться к константе из перечисления по имени, но тип этого перечисления не содержит константу с указанным именем.
Exception Класс Exception и его подклассы являются подклассами Throwable и указывают на ситуации, которые могут быть перехвачены приложением.
IllegalAccessException Выбрасывается, когда приложение совершает попытку применить рефлексию, чтобы создать экземпляр (отличный от массива), присвоить полю значение или получить значение поля, вызвать метод, но текущий выполняемый метод не имеет доступа к определению указанного класса, поля, метода или конструктора.
IllegalArgumentException Выбрасывается, когда методу передан недопустимый или неприемлемый аргумент.
IllegalMonitorStateException Выбрасывается, когда нить пытается ожидать монитор объекта или отправить оповещение другим нитям, ожидающим монитор объекта, но указанный монитор не принадлежит ей.
IllegalStateException Выбрасывается, когда метод вызван в недопустимое или неприемлемое время.
IllegalThreadStateException Выбрасывается, когда нить находится в неприемлемом состоянии для выполнения запрошенной операции.
IndexOutOfBoundsException Выбрасывается, когда индекс определенного типа (например, для массива, строки или вектора) находится вне допустимого диапазона.
InstantiationException Выбрасывается, когда приложение совершает попытку создать экземпляр класса с использованием метода newInstance класса Class, но создать экземпляр указанного класса невозможно.
InterruptedException Выбрасывается, когда нить находится в состоянии ожидания, сна или занята каким-либо другим образом, но ее работа прервана либо перед выполнением действия, либо после его выполнения.
NegativeArraySizeException Выбрасывается, когда приложение совершает попытку создать массив отрицательного размера.
NoSuchFieldException Выбрасывается, когда в классе отсутствует поле с указанным именем.
NoSuchMethodException Выбрасывается, когда невозможно найти указанный метод.
NullPointerException Выбрасывается, когда приложение совершает попытку использовать значение null, но требуется указать объект.
NumberFormatException Выбрасывается, когда приложение совершает попытку преобразовать строку в один из числовых типов, но строка имеет недопустимый формат.
RuntimeException Это родительский класс для тех исключений, которые могут быть выброшены при нормальной работе виртуальной машины Java.
SecurityException Выбрасывается менеджером безопасности при нарушении безопасности.
StringIndexOutOfBoundsException Выбрасывается методами класса String при попытке использовать отрицательный индекс или индекс, превышающий размер строки.
TypeNotPresentException Выбрасывается, когда приложение совершает попытку доступа к типу с указанием его имени в виде строки, но не удается найти определение типа с указанным именем.
UnsupportedOperationException Выбрасывается, когда запрошенная операция не поддерживается.

Пользовательские исключения

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

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

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

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

Пример исключения с конструктором по умолчанию:

class MyException extends Exception{
    // Конструктор по умолчанию
    MyException(){}
}

class Main{
    public static void main(String[] args){
        try{
            MyException e = new MyException();
            throw e;
        }
        catch(MyException ex){
             System.out.println("Перехвачено пользовательское исключение"); 
        }
    }
}

Этот код выведет следующий текст: “Перехвачено пользовательское исключение”.

Пример исключения с параметром в конструкторе:

class MyException extends Exception{
    MyException(String msg){
        super(msg);
    }
}

class Main{
    public static void main(String[] args){
        try{
            MyException e = new MyException("Перехвачено пользовательское исключение с информацией");
            throw e;
        }
        catch(MyException ex){
             System.out.println(ex.getMessage()); 
        }
    }
}

Вывод будет таким:   “Перехвачено пользовательское исключение с информацией”

Заключение

Исключения в Java позволяют указать пути обхода проблем и исправления их последствий. С их помощью можно поместить логику обработки исключительных ситуаций в отдельные блоки кода (catch), оставив основную логику в блоке try, а логику завершающих действий по обработке исключения – в блоке finally. Исключения могут быть обработаны в методах, где они возникают, или переданы далее с помощью ключевых throw и throws.

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

Ошибки в Java могут быть разбиты на две категории: ошибки времени разработки и логические ошибки. В этом уроке вы научитесь обработки исключений с помощью оператора try … catch.

обработка исключений java

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

Логические ошибки — это те, которые вы делаете как программист. Программа запустится, но, поскольку вы сделали что-то не так в коде, есть большая вероятность, что произойдет сбой. Вскоре вы увидите примеры ошибок во время выполнения. Вы также узнаете, как с ними справляться. Но сначала немного о том, как Java обрабатывает ошибки.

Java обработка исключений оператором try … catch

В Java ошибки обрабатываются объектом Exception (исключение). Исключения выбрасывают ошибки, а ваша задача — поймать их. Вы можете сделать это с помощью оператора try … catch.

Блок try … catch выглядит так:

try {

}
catch (ТипИсключения переменная_ошибки) {

}

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

В качестве примера обработки исключений в Java создайте новое консольное приложение. Назовите его как вам угодно. В коде метода Main введите следующее:

try {
    int a = 5;
    int b = 0;
    int c = a / b;
        
    System.out.println(c);
}
catch (Exception show_error) {
    System.out.println(show_error.getMessage());
}

В части try блока try … catch, мы создали три целых числа a, b и c. Мы хотим разделить a на b, а затем распечатать ответ.

Если что-то пойдет не так, у нас есть «план B». В круглых скобках мы написали это:

Exception show_error

Exception – это тип исключения, которое мы используем. В нашем случае это Exception объекта ошибки. Это тип исключения «поймать все» (catch all) и не самая лучшая практика программирования. Мы заменим Exception для обработки наших исключений Java на определенный тип в конце статьи.

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

В фигурных скобках catch у нас есть оператор println. Но главное, что мы поставили в круглых скобках println:

show_error.getMessage() 

getMessage — это метод, доступный объектам Exception. Как следует из его названия, он получает сообщение об ошибке, связанное с исключением.

Запустите программу и протестируйте ее. Ваш код должен выглядеть примерно так:

package errors;

public class ErrorsHand {

    public static void main(String[] args) {
        
        try {
            int a = 5;
            int b = 0;
            int c = a / b;

            System.out.println(c);
        }
        catch (Exception show_error) {
            System.out.println(show_error.getMessage());
        }
    }
    
}

И в окне «Вывод» должно отображаться следующее:

обработка исключений java пример

Другими словами, ошибка деления на ноль. Java не позволит вам разделить число на ноль, отсюда и сообщение об ошибке. Итак, мы совершили обработку исключений с помощью оператора try … catch.

Измените в коде на это:

double a = 5.0;
double b = 0;
double c = a / b;

Остальная часть кода может остаться прежней. Запустите эту программу и протестируйте ее.

Опять же, сообщение об ошибке будет отображаться в окне вывода:

обработка исключений java пример 2

На этот раз Java останавливает программу, потому что результатом будет бесконечно большое число (Infinity).

Ошибки, связанные с числами, на самом деле не должны проходить обработку типом исключения «catch all». Существует определенный тип для такой обработки, называемый ArithmeticException. Удалите слово Exception в круглых скобках вашего блока catch. Замените его на ArithmeticException. Теперь запустите программу снова.

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

Типы исключений

Исключения могут быть контролируемыми и неконтролируемыми. Разница состоит в следующем. Если метод может выбрасывать (генерировать) контролируемое исключение, и обработка этого исключения в методе не предусмотрена, то в сигнатуре этого метода необходимо указать ключевое слово throws и тип контролируемого исключения.

Самые распространенные неконтролируемые исключения Java

Тип исключения С чем связана ошибка
ArithmeticException Выполнение арифметических операций (например, деление на ноль)
ArrayIndexOutOfBoundsException Индекс массива выходит за допустимые границы
ArrayStoreException Присвоение элементу массива значения несовместимого типа
ClassCastException Попытка выполнить приведение несовместимых типов
IllegalArgumentException Методу указан неправильный аргумент
IllegalMonitorStateException Работа с монитором (относится к многопоточному программированию)
IllegalStateException Ресурс находится в некорректном состоянии
IllegalThreadStateException Предпринята попытка выполнить некорректную операцию на потоке
IndexOutOfBoundsException Индекс выходит за допустимый диапазон значений
NegativeArraySizeException Попытка создать массив отрицательного размера
NullPointerException Некорректное использование ссылок (обычно когда объектная переменная содержит пустую ссылку)
NumberFormatException Преобразование строки к числовому значению (когда в число преобразуется строка, содержащая некорректное текстовое представление числа)
SecurityException Попытка нарушить режим защиты
StringIndexOutOfBoundsException Неверное индексирование при работе с текстовой строкой
UnsupportedOperationException Попытка выполнить некорректную операцию

Контролируемые исключения Java

Тип исключения С чем связана ошибка
ClassNotFoundException Невозможно найти нужный класс
CloneNotSupportedException Некорректная попытка клонировать объект
IllegalAccessException Ошибка доступа к ресурсу
InstantiationException Попытка создать объект абстрактного класса или интерфейса
InterruptedException Прерывание одного потока другим
NoSuchFieldException Отсутствует нужное поле
NoSuchMethodException Отсутствует нужный метод

В следующем уроке вы узнаете, что такое отладка в Java.

Классы Java для обработки исключительных ситуаций из пакета java.lang. Методы класса Throwable. Примеры


Содержание

  • 1. Типы исключений, которые поддерживаются системой обработки исключений Java
  • 2. Классификация исключений по признаку наличия в операторе throws. Непроверяемые исключения. Проверяемые исключения
  • 3. Перечень подклассов непроверяемых исключений из пакета java.lang
  • 4. Проверяемые исключения из пакета java.lang
  • 5. Какое назначение класса Throwable? Методы класса Throwable
  • 6. Пример использования некоторых методов класса Throwable. Разработка собственного класса исключения
  • Связанные темы

Поиск на других ресурсах:

1. Типы исключений, которые поддерживаются системой обработки исключений Java

В языке программирования Java разработан эффективный механизм обработки исключений. В основе этого механизма лежат классы образующие иерархию. Для всех классов исключений Java базовым классом есть класс Throwable.

Из класса Throwable унаследованы два основных класса:

  • Exception – предназначен для задания исключительных условий, которые перехватываются программой. Если нужно объявить собственный класс (тип) исключений, то этот класс может быть унаследован от класса Exception;
  • Error – класс, который предназначен для описания исключений (ошибок) возникающих в самой среде Java. Такие исключения не оговариваются во время нормального выполнения прикладной программы. Примеры системных ошибок: недостаточно памяти, переполнение стека.

Схема верхнего уровня иерархии классов Java приведена на рисунке

Java. Вершина иерархии классов исключений

Рисунок. Вершина иерархии классов исключений Java

 

2. Классификация исключений по признаку наличия в операторе throws. Непроверяемые исключения. Проверяемые исключения

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

Более подробно о работе оператора throws описывается в теме:

  • Операторы throw, throws. Примеры

Если тип сгенерированного исключения есть подклассом стандартного класса RuntimeException, то не обязательно указывать этот тип в перечне оператора throws метода. Такое исключение называется непроверяемым исключением. В этом случае компилятор не проверяет обрабатываются или генерируются такие исключения в некотором месте программы.

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

 

3. Перечень подклассов непроверяемых исключений из пакета java.lang

Среди всего разнообразия классов и интерфейсов пакет java.lang содержит мощный арсенал классов для обработки исключений. Эти классы и интерфейсы составляют основу всех программ на Java. Пакет java.lang автоматически импортируется во все программы.

Ниже приведен перечень подклассов непроверяемых исключений производными от класса RuntimeException и которые определены в пакете java.lang:

  • ArithmeticException – арифметическая ошибка (например, деление на ноль);
  • ArrayIndexOutOfBoundsException – индекс за пределами массива;
  • ArrayStoreException – присваивание элементу массива объекта несовместимого типа;
  • ClassCastException – неправильное приведение типов;
  • EnumConstantNotPresent – попытка воспользоваться неопределенным значением перечисления;
  • IllegalArgumentException – недопустимый аргумент при вызове метода;
  • IllegalMonitorStateException – недопустимая контрольная операция;
  • IllegalStateException – неверное состояние среды или приложения;
  • IllegalThreadStateException – несовместимость запрашиваемой операции с текущим состоянием потока выполнения;
  • IndexOutOfBoundsException – выход индекса некоторого типа за допустимые границы;
  • NegativeArraySizeException – создание массива отрицательного размера;
  • NullPointerException – неправильное использование пустой ссылки;
  • NumberFormatException – неправильное преобразование символьной строки в числовой формат;
  • SecurityException – попытка нарушения безопасности;
  • StringIndexOutOfBounds – попытка доступа по индексу за пределами символьной строки;
  • TypeNotPresentException – тип не найден;
  • UnsupportedOperationException – найдена неподдерживаемая операция.

 

4. Проверяемые исключения из пакета java.lang

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

В языке Java в пакете java.lang реализован ряд проверяемых исключений. Ниже приведен их перечень:

  • ClassNotFoundException – класс не найден;
  • CloneNotSupportedException – попытка клонировать объект из класса, который не реализует интерфейс Cloneable;
  • IllegalAccessException – запрещен доступ к классу;
  • InstantiationException – попытка создать объект абстрактного класса или интерфейса;
  • InterruptedException – один поток выполнения прерван другим потоком;
  • NoSuchFieldException – запрашиваемое поле не существует;
  • NoSuchMethodException – запрашиваемый метод не существует;
  • ReflectiveOperationException – суперкласс исключений, связанных с рефлексией.

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



 

5. Какое назначение класса Throwable? Методы класса Throwable

Класс Throwable есть базовым для всех стандартных классов исключений Java. Этот класс предоставляет ряд методов, которые можно использовать или переопределять в собственных классах обработки исключений. Эти классы должны быть унаследованы от класса Exception, который унаследован от класса Throwable (см. рисунок). Класс Exception не содержит методов.
Ниже приведен перечень методов класса Throwable.

1. Метод

final void addSuppressed(Throwable исключение)

добавляет заданное исключение в список подавляемых исключений. Этот список связывается с вызывающим (данным) исключением. Метод используется для применения в операторе try с ресурсами.

2. Метод

Throwable fillInStackTrace()

возвращает объект класса Throwable, содержащий полную трассировку стека. Этот объект может быть сгенерирован повторно.

3. Метод

Throwable getCause()

возвращает исключение, лежащее в основе текущего исключения. Метод возвращает null в случае, если такое исключение отсутствует. Этот метод используется при создании цепочек исключений – он вызывает исключение, вызывающее текущее исключение.

4. Метод

String getLocalizedMessage()

возвращает локализованное описание исключения.

5. Метод

String getMessage()

возвращает описание исключения.

6. Метод

StackTraceElement[] getStackTrace()

возвращает массив, содержащий поэлементную трассировку стека в виде объектов класса StackTraceElement.

7. Метод

final Throwable[] getSuppressed()

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

8. Метод

Throwable initCause(Throwable причина_исключения)

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

9. Метод

printStackTrace();

выводит трассировку стека.

10. Метод printStackTrace() имеет еще две перегруженных реализации

void printStackTrace(PrintStream поток_вывода)
void printStackTrace(PrintWriter поток_вывода)

Метод направляет трассировку стека в заданный поток_вывода.

11. Метод

void setStackTrace(StackTraceElement элементы[])

устанавливает трассировку стека для заданных элементов.

12. Метод

String toString()

возвращает объект типа String содержащий описание исключения. Этот метод можно вызвать из метода println() при выводе объекта типа Throwable.

 



6. Пример использования некоторых методов класса Throwable. Разработка собственного класса исключения

В примере демонстрируется использование некоторых методов класса Throwable:

  • getLocalizedMessage();
  • getMessage();
  • toString();
  • getStackTrace();
  • fillInStackTrace().

Объявляется класс MyException, унаследованный от класса Exception. В иерархии классов исключений Java класс Exception унаследован от класса Throwable. Поэтому, класс MyException может использовать и переопределять методы класса Throwable.

Текст программы следующий:

import java.util.Scanner;

// собственный класс исключения, унаследован от Exception
class MyException extends Exception
{
  // переопределенная функция getLocalizedMessage()
  public String getLocalizedMessage()
  {
    return "MyException.getLocalizedMessage()";
  }
}

// класс, содержащий функцию main()
public class Train04 {
  // функция main() тестирует работу класса MyException
  public static void main(String[] args) {
    // Ввести число x. Если число за пределами [0..100],
    // то сгенерировать исключение MyException
    int x;
    Scanner sc = new Scanner(System.in);
    System.out.print("x = ");
    x = sc.nextInt(); // ввести x

    try {
      // сгенерировать исключение (создать объект типа MyException),
      // если x за пределами [0..100]
      if ((x<0)||(x>100))
        throw new MyException();
      System.out.println("OK!");
    }
    catch(MyException e)
    {
      // обработка исключения типа MyException,
      // демонстрация некоторых методов класса Throwable
      System.out.println("Return from getLocalizedMessage(): " + e.getLocalizedMessage());
      System.out.println("Return from getMessage(): " + e.getMessage());
      System.out.println("Method printStackTrace(): ");
      e.printStackTrace();
      System.out.println("Method toString(): " + e.toString());
      System.out.println("------------------------");
      System.out.println("Method getStackTrace(). Stack trace: ");
      StackTraceElement[] stE;

      stE = e.getStackTrace(); // метод getStackTrace()

      for (int i=0;i<stE.length;i++)
        System.out.println(stE[i].toString());

      System.out.println("-------------------------");
      System.out.println("Method fillStackTrace(). Stack trace: ");

      Throwable tA = e.fillInStackTrace();
      StackTraceElement[] stE2 = tA.getStackTrace();

      for (int i=0; i<stE2.length; i++)
        System.out.println(stE[i].toString());
      System.out.println("-------------------------");
    }
  }
}

Объясним некоторые фрагменты кода.

С целью демонстрации в классе MyException переопределяется метод getLocalizedMessage(). При вызове этого метода выводится сообщение из этого переопределенного метода. По данному примеру можно переопределять другие методы класса Throwable.

В функции main() продемонстрировано использование методов класса Throwable. Вводится переменная x, которая проверяется на допустимые значения в пределах то 0 до 100. Если значение x меньше 0 или больше 100, то генерируется исключение типа MyException.

Результат работы программы

x = 200
Return from getLocalizedMessage(): MyException.getLocalizedMessage()
Return from getMessage(): null
Method printStackTrace():
Method toString(): MyException: MyException.getLocalizedMessage()
------------------------
Method getStackTrace(). Stack trace:
Train04.main(Train04.java:36)
-------------------------
Method fillStackTrace(). Stack trace:
Train04.main(Train04.java:36)
-------------------------
MyException: MyException.getLocalizedMessage()

 


Связанные темы

  • Исключения. Исключительная ситуация. Ключевые слова trycatchfinallyПримеры
  • Операторы throw, throws. Примеры
  • Класс Exception. Создание собственных классов исключений. Примеры

 


Исключения

Exception

try
Оператор throw
Оператор throws
Оператор finally
Встроенные исключения Java
Создание собственных классов исключений

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

В методе, в котором происходит ошибка, создаётся и передаётся специальный объект. Метод может либо обработать исключение самостоятельно, либо пропустить его. В любом случае исключение ловится и обрабатывается. Исключение может появиться благодаря самой системе, либо вы сами можете создать его вручную. Системные исключения возникают при неправильном использовании языка Java или запрещённых приёмов доступа к системе. Ваши собственные исключения обрабатывают специфические ошибки вашей программы.

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

Существует пять ключевых слов, используемых в исключениях: try, catch, throw, throws, finally. Порядок обработки исключений следующий.

Операторы программы, которые вы хотите отслеживать, помещаются в блок try. Если исключение произошло, то оно создаётся и передаётся дальше. Ваш код может перехватить исключение при помощи блока catch и обработать его. Системные исключения автоматически передаются самой системой. Чтобы передать исключение вручную, используется throw. Любое исключение, созданное и передаваемое внутри метода, должно быть указано в его интерфейсе ключевым словом throws. Любой код, который следует выполнить обязательно после завершения блока try, помещается в блок finally

Схематически код выглядит так:


try {
    // блок кода, где отслеживаются ошибки
}
catch (тип_исключения_1 exceptionObject) {
    // обрабатываем ошибку
}
catch (тип_исключения_2 exceptionObject) {
    // обрабатываем ошибку
}
finally {
    // код, который нужно выполнить после завершения блока try
}

Существует специальный класс для исключений Trowable. В него входят два класса Exception и Error.

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

Класс Error служит для обработки ошибок в самом языке Java и на практике вам не придётся иметь с ним дело.

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


int catNumber;
int zero;
catNumber = 1; // у меня один кот
zero = 0; // ноль, он и в Африке ноль
int result = catNumber / zero;

Я поместил код в обработчик щелчка кнопки. Когда система времени выполнения Java обнаруживает попытку деления на ноль, она создаёт объект исключения и передаёт его. Да вот незадача, никто не перехватывает его, хотя это должны были сделать вы. Видя вашу бездеятельность, объект перехватывает стандартный системный обработчик Java, который отличается вредных характером. Он останавливает вашу программу и выводит сообщение об ошибке, которое можно увидеть в журнале LogCat:

Caused by: java.lang.ArithmeticException: divide by zero at ru.alexanderklimov.test.MainActivity.onClick(MainActivity.java:79)

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

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

Поместим проблемный код в блок try, а в блоке catch обработаем исключение.


int catNumber;
int zero;

try { // мониторим код
    catNumber = 1; // у меня один кот
    zero = 0; // ноль, он и в Африке ноль
    int result = catNumber / zero;
    Toast.makeText(this, "Не увидите это сообщение!", Toast.LENGTH_LONG).show();
} catch (ArithmeticException e) {
    Toast.makeText(this, "Нельзя котов делить на ноль!", Toast.LENGTH_LONG).show();
}
Toast.makeText(this, "Жизнь продолжается", Toast.LENGTH_LONG).show();

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

В данном случае мы уже знали, к какому классу принадлежит получаемая ошибка, поэтому в блоке catch сразу указали конкретный тип. Обратите внимание, что последний оператор в блоке try не срабатывает, так как ошибка происходит раньше строчкой выше. Далее выполнение передаётся в блок catch, далее выполняются следующие операторы в обычном порядке.

Операторы try и catch работают совместно в паре. Хотя возможны ситуации, когда catch может обрабатывать несколько вложенных операторов try.

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


catch (ArithmeticException e) {
	Toast.makeText(this, e + ": Нельзя котов делить на ноль!", Toast.LENGTH_LONG).show();
}

По умолчанию, класс Trowable, к которому относится ArithmeticException возвращает строку, содержащую описание исключения. Но вы можете и явно указать метод e.toString.

Несколько исключений

Фрагмент кода может содержать несколько проблемных мест. Например, кроме деления на ноль, возможна ошибка индексации массива. В таком случае вам нужно создать два или более операторов catch для каждого типа исключения. Причём они проверяются по порядку. Если исключение будет обнаружено у первого блока обработки, то он будет выполнен, а остальные проверки пропускаются и выполнение программы продолжается с места, который следует за блоком try/catch.


int catNumber;
int zero;

try { // мониторим код
    catNumber = 1; // у меня один кот
    zero = 1; // ноль, он и в Африке ноль
    int result = catNumber / zero;
    // Создадим массив из трёх котов
    String[] catNames = {"Васька", "Барсик", "Мурзик"};
    catNames[3] = "Рыжик";
    Toast.makeText(this, "Не увидите это сообщение!", Toast.LENGTH_LONG).show();
} catch (ArithmeticException e) {
    Toast.makeText(this, e.toString() + ": Нельзя котов делить на ноль!", Toast.LENGTH_LONG).show();
}
catch (ArrayIndexOutOfBoundsException e) {
	Toast.makeText(this, "Ошибка: " + e.toString(), Toast.LENGTH_LONG).show();
}
Toast.makeText(this, "Жизнь продолжается", Toast.LENGTH_LONG).show();

В примере мы добавили массив с тремя элементами, но обращаемся к четвёртому элементу, так как забыли, что отсчёт у массива начинается с нуля. Если оставить значение переменной zero равным нулю, то сработает обработка первого исключения деления на ноль, и мы даже не узнаем о существовании второй ошибки. Но допустим, что в результате каких-то вычислений значение переменной стало равно единице. Тогда наше исключение ArithmeticException не сработает. Но сработает новое добавленное исключение ArrayIndexOutOfBoundsException. А дальше всё пойдёт как раньше.

Тут всегда нужно помнить одну особенность. При использовании множественных операторов catch обработчики подклассов исключений должные находиться выше, чем обработчики их суперклассов. Иначе, суперкласс будет перехватывать все исключения, имея большую область перехвата. Иными словами, Exception не должен находиться выше ArithmeticException и ArrayIndexOutOfBoundsException. К счастью, среда разработки сама замечает непорядок и предупреждает вас, что такой порядок не годится. Увидев такую ошибку, попробуйте перенести блок обработки исключений ниже.

Вложенные операторы try

Операторы try могут быть вложенными. Если вложенный оператор try не имеет своего обработчика catch для определения исключения, то идёт поиск обработчика catch у внешнего блока try и т.д. Если подходящий catch не будет найден, то исключение обработает сама система (что никуда не годится).

Оператор throw

Часть исключений может обрабатывать сама система. Но можно создать собственные исключения при помощи оператора throw. Код выглядит так:


throw экземпляр_Throwable

Вам нужно создать экземпляр класса Throwable или его наследников. Получить объект класса Throwable можно в операторе catch или стандартным способом через оператор new.

Мы могли бы написать такой код для кнопки:


Cat cat;

public void onClick(View view) {
    if(cat == null){
        throw new NullPointerException("Котик не инициализирован");
    }
}

Мы объявили объект класса Cat, но забыли его проинициализировать, например, в onCreate(). Теперь нажатие кнопки вызовет исключение, которое обработает система, а в логах мы можем прочитать сообщение об ошибке. Возможно, вы захотите использовать другое исключение, например, throw new UnsupportedOperationException(«Котик не инициализирован»);.

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

Поток выполнения останавливается непосредственно после оператора throw и другие операторы не выполняются. При этом ищется ближайший блок try/catch соответствующего исключению типа.

Перепишем пример с обработкой ошибки.


public void onClick(View view) {
    if (cat == null) {
        try {
            throw new NullPointerException("Кота не существует");
        } catch (NullPointerException e) {
            Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
        }
    }
}

Мы создали новый объект класса NullPointerException. Многие классы исключений кроме стандартного конструктора по умолчанию с пустыми скобками имеют второй конструктор с строковым параметром, в котором можно разместить подходящую информацию об исключении. Получить текст из него можно через метод getMessage(), что мы и сделали в блоке catch.

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

Оператор throws

Если метод может породить исключение, которое он сам не обрабатывает, он должен задать это поведение так, чтобы вызывающий его код мог позаботиться об этом исключении. Для этого к объявлению метода добавляется конструкция throws, которая перечисляет типы исключений (кроме исключений Error и RuntimeException и их подклассов).

Общая форма объявления метода с оператором throws:


тип имя_метода(список_параметров) throws список_исключений {
    // код внутри метода
}

В фрагменте список_исключений можно указать список исключений через запятую.

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


// Метод без обработки исключения
public void createCat(){
	Toast.makeText(this, "Вы создали котёнка", Toast.LENGTH_LONG).show();
	throw new NullPointerException("Кота не существует");
}

// Щелчок кнопки
public void onClick(View v) {
	createCat();
}

Если вы запустите пример, то получите ошибку. Исправим код.


// Без изменений
public void createCat() throws NullPointerException {
	Toast.makeText(this, "Вы создали котёнка", Toast.LENGTH_LONG).show();
	throw new NullPointerException("Кота не существует");
}

// Щелчок кнопки
public void onClick(View v) {
	try {
		createCat();
	} catch (NullPointerException e) {
		// TODO: handle exception
		Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
	}
}

Мы поместили вызов метода в блок try и вызвали блок catch с нужным типом исключения. Теперь ошибки не будет.

Оператор finally

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

Ключевое слово finally создаёт блок кода, который будет выполнен после завершения блока try/catch, но перед кодом, следующим за ним. Блок будет выполнен, независимо от того, передано исключение или нет. Оператор finally не обязателен, однако каждый оператор try требует наличия либо catch, либо finally.

Существуют несколько готовых системных исключений. Большинство из них являются подклассами типа RuntimeException и их не нужно включать в список throws. Вот небольшой список непроверяемых исключений.

  • ArithmeticException — арифметическая ошибка, например, деление на нуль
  • ArrayIndexOutOfBoundsException — выход индекса за границу массива
  • ArrayStoreException — присваивание элементу массива объекта несовместимого типа
  • ClassCastException — неверное приведение
  • EnumConstantNotPresentException — попытка использования неопределённого значения перечисления
  • IllegalArgumentException — неверный аргумент при вызове метода
  • IllegalMonitorStateException — неверная операция мониторинга
  • IllegalStateException — некорректное состояние приложения
  • IllegalThreadStateException — запрашиваемая операция несовместима с текущим потоком
  • IndexOutofBoundsException — тип индекса вышел за допустимые пределы
  • NegativeArraySizeException — создан массив отрицательного размера
  • NullPointerException — неверное использование пустой ссылки
  • NumberFormatException — неверное преобразование строки в числовой формат
  • SecurityException — попытка нарушения безопасности
  • StringIndexOutOfBounds — попытка использования индекса за пределами строки
  • TypeNotPresentException — тип не найден
  • UnsupportedOperationException — обнаружена неподдерживаемая операция

Список проверяемых системных исключений, которые можно включать в список throws.

  • ClassNotFoundException — класс не найден
  • CloneNotSupportedException — попытка клонировать объект, который не реализует интерфейс Cloneable
  • IllegalAccessException — запрещен доступ к классу
  • InstantiationException — попытка создать объект абстрактного класса или интерфейса
  • InterruptedException — поток прерван другим потоком
  • NoSuchFieldException — запрашиваемое поле не существует
  • NoSuchMethodException — запрашиваемый метод не существует
  • ReflectiveOperationException — исключение, связанное с рефлексией

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

Система не может предусмотреть все исключения, иногда вам придётся создать собственный тип исключения для вашего приложения. Вам нужно наследоваться от Exception (напомню, что этот класс наследуется от Trowable) и переопределить нужные методы класса Throwable. Либо вы можете наследоваться от уже существующего типа, который наиболее близок по логике с вашим исключением.

  • final void addSuppressed(Throwable exception) — добавляет исключение в список подавляемых исключений (JDK 7)
  • Throwable fillInStackTrace() — возвращает объект класса Throwable, содержащий полную трассировку стека.
  • Throwable getCause() — возвращает исключение, лежащее под текущим исключение или null
  • String getLocalizedMessage() — возвращает локализованное описание исключения
  • String getMessage() — возвращает описание исключения
  • StackTraceElement[] getStackTrace() — возвращает массив, содержащий трассировку стека и состояний из элементов класса StackTraceElement
  • final Throwable[] getSuppressed() — получает подавленные исключения (JDK 7)
  • Throwable initCause(Throwable exception) — ассоциирует исключение с вызывающим исключением. Возвращает ссылку на исключение.
  • void printStackTrace() — отображает трассировку стека
  • void printStackTrace(PrintStream stream) — посылает трассировку стека в заданный поток
  • void printStackTrace(PrintWriter stream) — посылает трассировку стека в заданный поток
  • void setStackTrace(StackTraceElement elements[]) — устанавливает трассировку стека для элементов (для специализированных приложений)
  • String toString() — возвращает объект класса String, содержащий описание исключения.

Самый простой способ — создать класс с конструктором по умолчанию.


// Если этот код работает, его написал Александр Климов,
// а если нет, то не знаю, кто его писал.

package ru.alexanderklimov.exception;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void testMethod() throws HungryCatException{
        System.out.println("Возбуждаем HungryCatException из метода testMethod()");
        throw new HungryCatException(); // конструктор по умолчанию
    }

    public void onClick(View view) {
        try {
            testMethod();
        } catch (HungryCatException e) {
            e.printStackTrace();
            System.out.println("Наше исключение перехвачено");
        }
    }

    class HungryCatException extends Exception{
    }
}

Мы создали собственный класс HungryCatException, в методе testMethod() его возбуждаем, а по нажатию кнопки вызываем этот метод. В результате наше исключение сработает.

Создать класс исключения с конструктором, который получает аргумент-строку, также просто.


// Если этот код работает, его написал Александр Климов,
// а если нет, то не знаю, кто его писал.

package ru.alexanderklimov.exception;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void testMethod() throws HungryCatException {
        System.out.println("Возбуждаем HungryCatException из метода testMethod()");
        throw new HungryCatException(); // конструктор по умолчанию
    }

    public void testMethod2() throws HungryCatException {
        System.out.println("Возбуждаем HungryCatException из метода testMethod2()");
        throw new HungryCatException("Создано во втором методе");
    }

    public void onClick(View view) {
        try {
            testMethod();
        } catch (HungryCatException e) {
            e.printStackTrace();
            System.out.println("Наше исключение перехвачено");
        }

        try {
            testMethod2();
        } catch (HungryCatException e) {
            e.printStackTrace();
        }
    }

    class HungryCatException extends Exception {
        HungryCatException() {
        }

        HungryCatException(String msg) {
            super(msg);
        }
    }
}

Ещё вариант. Добавим также метод toString().


class CustomException extends Exception {
    String message;

    CustomException(String str) {
        message = str;
    }

    public String toString() {
        return ("Custom Exception Occurred: " + message);
    }
}

// где-то вызываем
try {
    throw new CustomException("This is a custom message");
} catch (CustomException e) {
    System.out.println(e);
}

Теперь класс содержит два конструктора. Во втором конструкторе используется конструктор родительского класса с аргументом String, вызываемый ключевым словом super.

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

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

cacth(Exception e) {
    Log.w("Log", "Перехвачено исключение");
}

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

Основные правила обработки исключений

Используйте исключения для того, чтобы:

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

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

  Исключения (ошибки при выполнении программы) могут возникать в результате:

   — неправильных действий пользователя

   — отсутствии необходимого ресурса на диске

   — потери соединения с сервером по сети

   — ошибки программирования

   — неправильное использование API.

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

   (чтобы предупредить и решить исключительные ситуации в программе, для того чтобы её выполнение могло быть продолжено)

   В частности, механизм исключений позволяет защитить написанный код
(программный интерфейс) от неправильного использования пользователем за
счет валидации (проверки) входящих данных. 

  Использование исключений в Java позволяет повысить отказоустойчивость
программы за счет использования «запасных» путей, отделить логику
основного кода от кода обработки исключительных ситуаций за счет
использования блоков catch, а также дает возможность переложить
обработку исключений на пользователя нашего кода с помощью throws.

   Обработка исключений в Java основана на использовании в программе следующих ключевых слов

try, catch, finally, throws

   1) try – определяет (заключает под собой) блок кода, в котором может произойти исключение;

   2) catch – определяет (заключает под собой) блок кода, в котором происходит обработка исключения;

   (Действия при сбое, который может случиться внутри оператора try)

   3) finally – определяет (заключает под собой) блок кода, который является необязательным, но при его наличии выполняется в любом случае независимо от результатов выполнения блока try.

(Блок finally часто используется для того, чтобы закрыть открытые в блоке try потоки или освободить ресурсы. Однако при написании программы не всегда возможно уследить за закрытием всех ресурсов. Для облегчения этой задачи существует конструкция try-with-resources, которая автоматически закрывает ресурсы, открытые в блоке try).

   Эти ключевые слова используются для создания в программном коде специальных обрабатывающих конструкций: try{}catch, try{}catch{}finally, try{}finally{}.

   На стадии разработки программы мы «ограждаем» опасные участки кода в отношении исключений с помощью блока try{}, предусматриваем «запасные» пути с помощью блока catch{}, в блоке finally{} мы пишем код, который выполняется в программе при любом исходе.

   При появлении исключения в блоке try обработчик исключения ищется в
следующем за ним блоке catch. Если в catch есть обработчик данного типа
исключения – управление переходит к нему. Если нет, то JVM ищет
обработчик этого типа исключения в цепочке вызовов методов до тех пор,
пока не будет найден подходящий catch. После выполнения блока catch
управление передается в необязательный блок finally.

В случае, если подходящий блок catch не найден, JVM останавливает
выполнение программы, и выводит стек вызовов методов – stack trace,
выполнив перед этим код блока finally при его наличии.

   Переменные, определенные в try не могут быть использованы в catch или finally, этот код не скомпилируется. Причина в том, что неизвестно, где именно в блоке try могло быть вызвано исключение, например, возможно, что исключение было вызвано до того, как был объявлен объект.

  4) throw – используется для возбуждения исключения

  5) throws – Это ключевое слово в сигнатуре метода означает, что при определенных условиях метод, может выбросить исключение

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

   Такое предупреждение является частью интерфейса метода и предоставляет право пользователю на собственный вариант реализации обработчика исключения (предоставляя написание кода по обработке исключения в Java пользователю метода.).

   После throws указывается тип выбрасываемого исключения.

   Типы выбрасываемых исключений:

   Обычно это наследники класса Exception Java.

   Поскольку Java является объектно-ориентированным языком, все исключения в Java представляют собой объекты.

   При возникновении ошибки в процессе выполнения программы исполняющая
среда JVM создает объект нужного типа из иерархии исключений Java.

   Иерархия исключений java:

   Throwable — название класса «предка», от которого унаследовано множество возможных исключительных ситуаций.

   Далее всё это множество исключительных ситуаций можно разделить на две группы:

   1) Непроверяемые на стадии компиляции (неконтролируемые) (unchecked)

   Это Error (исключения, унаследованные из класса Error) + RuntimeException (часть исключений — наследников
класса Exception)

  RuntimeException – исключения, генерируемые JVM во время выполнения программы. Часто
причиной возникновения их являются ошибки программирования.

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

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

Список непроверяемых исключений:

   ArithmeticException — арифметическая ошибка, например, деление на ноль

   ArrayIndexOutOfBoundsException — выход индекса за границу массива

   ArrayStoreException — присваивание элементу массива объекта несовместимого типа

   ClassCastException — неверное приведение

   EnumConstantNotPresentException — попытка использования неопределённого значения перечисления

   IllegalArgumentException — неверный аргумент при вызове метода

   IllegalMonitorStateException — неверная операция мониторинга

   IllegalStateException — некорректное состояние приложения

   IllegalThreadStateException — запрашиваемая операция несовместима с текущим потоком

   IndexOutofBoundsException — тип индекса вышел за допустимые пределы

   NegativeArraySizeException — создан массив отрицательного размера

   NullPointerException — неверное использование пустой ссылки

NullPointerException (оно же NPE)  — это исключение, которое
выбрасывается каждый раз, когда вы обращаетесь к методу или полю объекта
по ссылке, которая равна null.

   NumberFormatException — неверное преобразование строки в числовой формат
SecurityException — попытка нарушения безопасности

   StringIndexOutOfBounds — попытка использования индекса за пределами строки

   TypeNotPresentException — тип не найден

   UnsupportedOperationException — обнаружена неподдерживаемая операция

   2) Проверяемые на стадии компиляции (предвидимые еще на стадии написания программы) (контролируемые) (checked)

   Это Exception (кроме RuntimeException)

   Основная часть работы разработчика на Java при работе с исключениями – обработка таких ситуаций: проверяемые исключения должны быть явно пойманы в теле метода или объявлены в секции throws метода.

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

   Список проверяемых системных исключений, которые можно включать в список throws:

   ClassNotFoundException — класс не найден

   CloneNotSupportedException — попытка клонировать объект, который не реализует интерфейс Cloneable

   IllegalAccessException — запрещен доступ к классу

   InstantiationException — попытка создать объект абстрактного класса или интерфейса

   InterruptedException — поток прерван другим потоком

   NoSuchFieldException — запрашиваемое поле не существует

   NoSuchMethodException — запрашиваемый метод не существует

   ReflectiveOperationException — исключение, связанное с рефлексией

   Пример использования ключевых слов в java-программе:

//метод считывает строку с клавиатуры

public String input() throws MyException {//предупреждаем с помощью throws,
// что метод может выбросить исключение MyException
      BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    String s = null;
//в блок try заключаем код, в котором может произойти исключение, в данном
// случае компилятор нам подсказывает, что метод readLine() класса
// BufferedReader может выбросить исключение ввода/вывода
    try {
        s = reader.readLine();
// в блок  catch заключаем код по обработке исключения IOException
    } catch (IOException e) {
        System.out.println(e.getMessage());
// в блоке finally закрываем поток чтения
    } finally {
// при закрытии потока тоже возможно исключение, например, если он не был открыт, поэтому “оборачиваем” код в блок try
        try {
            reader.close();
// пишем обработку исключения при закрытии потока чтения
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }

    if (s.equals("")) {
// мы решили, что пустая строка может нарушить в дальнейшем работу нашей программы, например, на результате этого метода нам надо вызывать метод substring(1,2), поэтому мы вынуждены прервать выполнение программы с генерацией своего типа исключения MyException с помощью throw
        throw new MyException("String can not be empty!");
    }
    return s;
}
 

 Этот же пример с помощью try-with-resources:

public String input() throws MyException {
    String s = null;
    try(BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))){
        s = reader.readLine();
   } catch (IOException e) {
       System.out.println(e.getMessage());
   }
    if (s.equals("")){
        throw new MyException ("String can not be empty!");
    }
    return s;
}

Ошибки в Java могут быть разбиты на две категории: ошибки времени разработки и логические ошибки. В этом уроке вы научитесь обработки исключений с помощью оператора try … catch.

обработка исключений java

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

Логические ошибки — это те, которые вы делаете как программист. Программа запустится, но, поскольку вы сделали что-то не так в коде, есть большая вероятность, что произойдет сбой. Вскоре вы увидите примеры ошибок во время выполнения. Вы также узнаете, как с ними справляться. Но сначала немного о том, как Java обрабатывает ошибки.

Java обработка исключений оператором try … catch

В Java ошибки обрабатываются объектом Exception (исключение). Исключения выбрасывают ошибки, а ваша задача — поймать их. Вы можете сделать это с помощью оператора try … catch.

Блок try … catch выглядит так:

try {

}
catch (ТипИсключения переменная_ошибки) {

}

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

В качестве примера обработки исключений в Java создайте новое консольное приложение. Назовите его как вам угодно. В коде метода Main введите следующее:

try {
    int a = 5;
    int b = 0;
    int c = a / b;
        
    System.out.println(c);
}
catch (Exception show_error) {
    System.out.println(show_error.getMessage());
}

В части try блока try … catch, мы создали три целых числа a, b и c. Мы хотим разделить a на b, а затем распечатать ответ.

Если что-то пойдет не так, у нас есть «план B». В круглых скобках мы написали это:

Exception show_error

Exception – это тип исключения, которое мы используем. В нашем случае это Exception объекта ошибки. Это тип исключения «поймать все» (catch all) и не самая лучшая практика программирования. Мы заменим Exception для обработки наших исключений Java на определенный тип в конце статьи.

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

В фигурных скобках catch у нас есть оператор println. Но главное, что мы поставили в круглых скобках println:

show_error.getMessage() 

getMessage — это метод, доступный объектам Exception. Как следует из его названия, он получает сообщение об ошибке, связанное с исключением.

Запустите программу и протестируйте ее. Ваш код должен выглядеть примерно так:

package errors;

public class ErrorsHand {

    public static void main(String[] args) {
        
        try {
            int a = 5;
            int b = 0;
            int c = a / b;

            System.out.println(c);
        }
        catch (Exception show_error) {
            System.out.println(show_error.getMessage());
        }
    }
    
}

И в окне «Вывод» должно отображаться следующее:

обработка исключений java пример

Другими словами, ошибка деления на ноль. Java не позволит вам разделить число на ноль, отсюда и сообщение об ошибке. Итак, мы совершили обработку исключений с помощью оператора try … catch.

Измените в коде на это:

double a = 5.0;
double b = 0;
double c = a / b;

Остальная часть кода может остаться прежней. Запустите эту программу и протестируйте ее.

Опять же, сообщение об ошибке будет отображаться в окне вывода:

обработка исключений java пример 2

На этот раз Java останавливает программу, потому что результатом будет бесконечно большое число (Infinity).

Ошибки, связанные с числами, на самом деле не должны проходить обработку типом исключения «catch all». Существует определенный тип для такой обработки, называемый ArithmeticException. Удалите слово Exception в круглых скобках вашего блока catch. Замените его на ArithmeticException. Теперь запустите программу снова.

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

Типы исключений

Исключения могут быть контролируемыми и неконтролируемыми. Разница состоит в следующем. Если метод может выбрасывать (генерировать) контролируемое исключение, и обработка этого исключения в методе не предусмотрена, то в сигнатуре этого метода необходимо указать ключевое слово throws и тип контролируемого исключения.

Самые распространенные неконтролируемые исключения Java

Тип исключения С чем связана ошибка
ArithmeticException Выполнение арифметических операций (например, деление на ноль)
ArrayIndexOutOfBoundsException Индекс массива выходит за допустимые границы
ArrayStoreException Присвоение элементу массива значения несовместимого типа
ClassCastException Попытка выполнить приведение несовместимых типов
IllegalArgumentException Методу указан неправильный аргумент
IllegalMonitorStateException Работа с монитором (относится к многопоточному программированию)
IllegalStateException Ресурс находится в некорректном состоянии
IllegalThreadStateException Предпринята попытка выполнить некорректную операцию на потоке
IndexOutOfBoundsException Индекс выходит за допустимый диапазон значений
NegativeArraySizeException Попытка создать массив отрицательного размера
NullPointerException Некорректное использование ссылок (обычно когда объектная переменная содержит пустую ссылку)
NumberFormatException Преобразование строки к числовому значению (когда в число преобразуется строка, содержащая некорректное текстовое представление числа)
SecurityException Попытка нарушить режим защиты
StringIndexOutOfBoundsException Неверное индексирование при работе с текстовой строкой
UnsupportedOperationException Попытка выполнить некорректную операцию

Контролируемые исключения Java

Тип исключения С чем связана ошибка
ClassNotFoundException Невозможно найти нужный класс
CloneNotSupportedException Некорректная попытка клонировать объект
IllegalAccessException Ошибка доступа к ресурсу
InstantiationException Попытка создать объект абстрактного класса или интерфейса
InterruptedException Прерывание одного потока другим
NoSuchFieldException Отсутствует нужное поле
NoSuchMethodException Отсутствует нужный метод

В следующем уроке вы узнаете, что такое отладка в Java.

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

Когда может понадобиться обработка исключений

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

java исключения

Обработка исключений Java

Что необходимо учитывать? Язык Java обладает своим встроенным функционалом обработки исключений. Конечно же большой процент ошибок отлавливается ещё на стадии компиляции, когда система автоматически сообщит о том, что использовать её дальше невозможно. Но существует и такой вид исключений, который возникает во время работы программы. Разработчик должен суметь предвидеть это и спроектировать код таким образом, чтобы он не вызвал ошибки, а обработал её особым способом или передал управление в другую ветку.

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

Типичные исключения

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

В Java механизм обработки перехвата ошибки выглядит так:

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

Простейший пример создания ошибки может выглядеть таким образом:

if (a == null)

throw new NullPointerException();

Здесь переменная a проверяется на инициализацию, т.е. не равна ли ссылка на объект null. В случае, если такая ситуация возникла и нужна особая обработка, выбрасывается исключение с помощью throw new NullPointerException().

Немного подробностей о ключевых словах

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

  • Try. Это ключевое слово уже встречалось и означает оно переход в участок кода, который может сгенерировать исключение. Блок ограничивается фигурными скобками {}.
  • Catch. Перехватывает нужный тип исключения и обрабатывает его соответствующим образом.
  • Finally. Данное ключевое слово является дополнительным и служит для выполнения некоего участка кода, который необходим в любом случае, даже если ни одно исключение не перехвачено. Добавляется непосредственно после блока try.
  • Throw — позволяет создавать исключения Java в любом месте кода.
  • Throws — ключевое слово, которое ставится в сигнатуре метода. Оно означает, что последующий код может выбросить исключение Java указанного типа. Такая метка служит сигналом для разработчиков, что нужно иметь в виду — метод может сработать не так, как от него ожидают.

Отлов с помощью try

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

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

Самый типичный код с блоком обработки выглядит так:

try {

//Здесь будет определён код, который может породить исключение

} catch (Тип_исключения_1 идентификатор_1) {

//Здесь происходит обработка исключения, согласно его типу и условиям;

} catch (Тип_исключения_2 идентификатор_2) {

//Здесь происходит обработка исключения, согласно его типу и условиям;

}

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

Finally

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

обработка исключений java

В данном участке представлены несколько блоков catch с придуманными методами отлова исключений. К примеру, код, содержащийся в try порождает непредвиденную ситуацию типа Cold. Тогда в консоль будут выведены выражения «Caught cold!» и «Is that something to cheer about?». То есть блок finally выполняется в любом случае.

На самом деле способ избежать запуска finally существует. Связан он с завершением работы виртуальной машины. Найти, как это реализовать, можно на просторах сети Интернет.

Ключевое слово throw

Throw генерирует исключение. Его синтаксис выглядит так:

throw new NewException();

Здесь создаётся новое исключение с типом NewException(). В качестве типа могут использоваться уже входящие в стандартные библиотеки Java классы и определённые ранее разработчиком собственного производства.

Такая конструкция входит в описание какого-либо метода, вызов которого затем должен происходить в рамках блока try, для того, чтобы была возможность его перехватить.

Ключевое слово throws

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

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

Try с ресурсами

В Java версии 7 разработчики включили такое важное нововведение, как обработка блока try с ресурсами.

Многие создаваемые объекты в Java, после их использования должны быть закрыты для экономии ресурсов. Раньше приходилось это учитывать и останавливать такие экземпляры вручную. Теперь же в них появился интерфейс AutoClosable. Он помогает автоматически закрывать уже использованные объекты, помещённые в блок try. Благодаря такому подходу писать код стало удобней, в его читаемость значительно повысилась.

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

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

Простейший способ создания — унаследовать от наиболее подходящего к контексту объекта.

java исключения список

Здесь произошло наследование от Exception, класса, который используется для определения собственных исключений. В MyException имеются два конструктора — один по умолчанию, второй — с аргументом msg типа String.

Затем в public классе FullConstructors реализован метод f, сигнатура которого содержит throws MyException. Это ключевое слово означает, что f может выбросить исключение Java типа MyException. Далее в теле метода производится вывод текстовой информации в консоль и собственно сама генерация MyException, посредством throw.

Второй метод немного отличается от первого тем, что при создании исключения, ему передается строковый параметр, который будет отражён в консоли при отлове. В main видно, что f() и g() помещены в блок проверки try, а ключевое слово catch настроено на отлов MyException. Результатом обработки будет вывод сообщения об ошибке в консоль:

создавать исключения java

Таким образом получилось добавить исключения Java, созданные собственноручно.

Архитектура исключений

Как и все объекты в Java, исключения также наследуются и имеют иерархическую структуру. Корневым элементом всех ошибок, выбрасываемых в этом языке программирования является класс java.lang.Throwable. От него наследуются два вида — Error и Exception.

 выбросить исключение java

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

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

Часто встречаемые исключения

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

  • ArithmeticException. Сюда входят ошибки связанные с арифметическими операциями. Самый яркий пример — деление на ноль.
  • ArrayIndexOutOfBoundsException — обращение к номеру элемента массива, который превышает общую его длину.
  • ArrayStoreException — попытка присвоить элементу массива несовместимого типа.
  • ClassCastException — попытка неправильного приведения одного типа к другому.
  • IllegalArgumentException — использование неправильного аргумента в вызове метода.
  • NegativeArraySizeException — исключение при создании массива отрицательного размера.
  • NullPointerException — неправильное использование ссылки на null.
  • NumberFormatException — возникает при неверном преобразовании строки в число.
  • UnsupportedOperationException — операция не поддерживается.

Данные примеры представляют собой непроверяемые типы исключений Java. А вот так выглядят проверяемые:

  • ClassNotFoundException — класс не обнаружен.
  • IllegalAcccessException — ограничение доступа к классу.
  • InterruptedException — прерывание работы потока.
  • NoSuchFieldException — не существует требуемое поле.

Интерпретация исключений

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

типы исключений java

NullPointerException. Самым первым случаем, когда возникает исключение, является обращение к ссылке на объект, которая равна null. Также это распространяется на методы нулевого экземпляра класса. NullPointerException может быть брошен и в случае получения длины массива равной null. Избежать таких ситуаций поможет периодическая проверка объектов на null.

ArrayIndexOutOfBoundsException. Любая программа не может существовать без использования массивов. Соответственно, частое обращение к ним может порождать и ошибки. Возникает исключение, когда разработчик пытается обратиться к элементу массива, который отсутствует в списке индексов. Например, запрашиваемое значение выше длины или меньше нуля. Очень часто появляется в результате того, что счёт в массиве начинается с нуля.

Выводы

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

 java исключения ошибки

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

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

Исключения

Exception

try
Оператор throw
Оператор throws
Оператор finally
Встроенные исключения Java
Создание собственных классов исключений

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

В методе, в котором происходит ошибка, создаётся и передаётся специальный объект. Метод может либо обработать исключение самостоятельно, либо пропустить его. В любом случае исключение ловится и обрабатывается. Исключение может появиться благодаря самой системе, либо вы сами можете создать его вручную. Системные исключения возникают при неправильном использовании языка Java или запрещённых приёмов доступа к системе. Ваши собственные исключения обрабатывают специфические ошибки вашей программы.

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

Существует пять ключевых слов, используемых в исключениях: try, catch, throw, throws, finally. Порядок обработки исключений следующий.

Операторы программы, которые вы хотите отслеживать, помещаются в блок try. Если исключение произошло, то оно создаётся и передаётся дальше. Ваш код может перехватить исключение при помощи блока catch и обработать его. Системные исключения автоматически передаются самой системой. Чтобы передать исключение вручную, используется throw. Любое исключение, созданное и передаваемое внутри метода, должно быть указано в его интерфейсе ключевым словом throws. Любой код, который следует выполнить обязательно после завершения блока try, помещается в блок finally

Схематически код выглядит так:


try {
    // блок кода, где отслеживаются ошибки
}
catch (тип_исключения_1 exceptionObject) {
    // обрабатываем ошибку
}
catch (тип_исключения_2 exceptionObject) {
    // обрабатываем ошибку
}
finally {
    // код, который нужно выполнить после завершения блока try
}

Существует специальный класс для исключений Trowable. В него входят два класса Exception и Error.

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

Класс Error служит для обработки ошибок в самом языке Java и на практике вам не придётся иметь с ним дело.

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


int catNumber;
int zero;
catNumber = 1; // у меня один кот
zero = 0; // ноль, он и в Африке ноль
int result = catNumber / zero;

Я поместил код в обработчик щелчка кнопки. Когда система времени выполнения Java обнаруживает попытку деления на ноль, она создаёт объект исключения и передаёт его. Да вот незадача, никто не перехватывает его, хотя это должны были сделать вы. Видя вашу бездеятельность, объект перехватывает стандартный системный обработчик Java, который отличается вредных характером. Он останавливает вашу программу и выводит сообщение об ошибке, которое можно увидеть в журнале LogCat:

Caused by: java.lang.ArithmeticException: divide by zero at ru.alexanderklimov.test.MainActivity.onClick(MainActivity.java:79)

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

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

Поместим проблемный код в блок try, а в блоке catch обработаем исключение.


int catNumber;
int zero;

try { // мониторим код
    catNumber = 1; // у меня один кот
    zero = 0; // ноль, он и в Африке ноль
    int result = catNumber / zero;
    Toast.makeText(this, "Не увидите это сообщение!", Toast.LENGTH_LONG).show();
} catch (ArithmeticException e) {
    Toast.makeText(this, "Нельзя котов делить на ноль!", Toast.LENGTH_LONG).show();
}
Toast.makeText(this, "Жизнь продолжается", Toast.LENGTH_LONG).show();

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

В данном случае мы уже знали, к какому классу принадлежит получаемая ошибка, поэтому в блоке catch сразу указали конкретный тип. Обратите внимание, что последний оператор в блоке try не срабатывает, так как ошибка происходит раньше строчкой выше. Далее выполнение передаётся в блок catch, далее выполняются следующие операторы в обычном порядке.

Операторы try и catch работают совместно в паре. Хотя возможны ситуации, когда catch может обрабатывать несколько вложенных операторов try.

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


catch (ArithmeticException e) {
	Toast.makeText(this, e + ": Нельзя котов делить на ноль!", Toast.LENGTH_LONG).show();
}

По умолчанию, класс Trowable, к которому относится ArithmeticException возвращает строку, содержащую описание исключения. Но вы можете и явно указать метод e.toString.

Несколько исключений

Фрагмент кода может содержать несколько проблемных мест. Например, кроме деления на ноль, возможна ошибка индексации массива. В таком случае вам нужно создать два или более операторов catch для каждого типа исключения. Причём они проверяются по порядку. Если исключение будет обнаружено у первого блока обработки, то он будет выполнен, а остальные проверки пропускаются и выполнение программы продолжается с места, который следует за блоком try/catch.


int catNumber;
int zero;

try { // мониторим код
    catNumber = 1; // у меня один кот
    zero = 1; // ноль, он и в Африке ноль
    int result = catNumber / zero;
    // Создадим массив из трёх котов
    String[] catNames = {"Васька", "Барсик", "Мурзик"};
    catNames[3] = "Рыжик";
    Toast.makeText(this, "Не увидите это сообщение!", Toast.LENGTH_LONG).show();
} catch (ArithmeticException e) {
    Toast.makeText(this, e.toString() + ": Нельзя котов делить на ноль!", Toast.LENGTH_LONG).show();
}
catch (ArrayIndexOutOfBoundsException e) {
	Toast.makeText(this, "Ошибка: " + e.toString(), Toast.LENGTH_LONG).show();
}
Toast.makeText(this, "Жизнь продолжается", Toast.LENGTH_LONG).show();

В примере мы добавили массив с тремя элементами, но обращаемся к четвёртому элементу, так как забыли, что отсчёт у массива начинается с нуля. Если оставить значение переменной zero равным нулю, то сработает обработка первого исключения деления на ноль, и мы даже не узнаем о существовании второй ошибки. Но допустим, что в результате каких-то вычислений значение переменной стало равно единице. Тогда наше исключение ArithmeticException не сработает. Но сработает новое добавленное исключение ArrayIndexOutOfBoundsException. А дальше всё пойдёт как раньше.

Тут всегда нужно помнить одну особенность. При использовании множественных операторов catch обработчики подклассов исключений должные находиться выше, чем обработчики их суперклассов. Иначе, суперкласс будет перехватывать все исключения, имея большую область перехвата. Иными словами, Exception не должен находиться выше ArithmeticException и ArrayIndexOutOfBoundsException. К счастью, среда разработки сама замечает непорядок и предупреждает вас, что такой порядок не годится. Увидев такую ошибку, попробуйте перенести блок обработки исключений ниже.

Вложенные операторы try

Операторы try могут быть вложенными. Если вложенный оператор try не имеет своего обработчика catch для определения исключения, то идёт поиск обработчика catch у внешнего блока try и т.д. Если подходящий catch не будет найден, то исключение обработает сама система (что никуда не годится).

Оператор throw

Часть исключений может обрабатывать сама система. Но можно создать собственные исключения при помощи оператора throw. Код выглядит так:


throw экземпляр_Throwable

Вам нужно создать экземпляр класса Throwable или его наследников. Получить объект класса Throwable можно в операторе catch или стандартным способом через оператор new.

Мы могли бы написать такой код для кнопки:


Cat cat;

public void onClick(View view) {
    if(cat == null){
        throw new NullPointerException("Котик не инициализирован");
    }
}

Мы объявили объект класса Cat, но забыли его проинициализировать, например, в onCreate(). Теперь нажатие кнопки вызовет исключение, которое обработает система, а в логах мы можем прочитать сообщение об ошибке. Возможно, вы захотите использовать другое исключение, например, throw new UnsupportedOperationException(«Котик не инициализирован»);.

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

Поток выполнения останавливается непосредственно после оператора throw и другие операторы не выполняются. При этом ищется ближайший блок try/catch соответствующего исключению типа.

Перепишем пример с обработкой ошибки.


public void onClick(View view) {
    if (cat == null) {
        try {
            throw new NullPointerException("Кота не существует");
        } catch (NullPointerException e) {
            Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
        }
    }
}

Мы создали новый объект класса NullPointerException. Многие классы исключений кроме стандартного конструктора по умолчанию с пустыми скобками имеют второй конструктор с строковым параметром, в котором можно разместить подходящую информацию об исключении. Получить текст из него можно через метод getMessage(), что мы и сделали в блоке catch.

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

Оператор throws

Если метод может породить исключение, которое он сам не обрабатывает, он должен задать это поведение так, чтобы вызывающий его код мог позаботиться об этом исключении. Для этого к объявлению метода добавляется конструкция throws, которая перечисляет типы исключений (кроме исключений Error и RuntimeException и их подклассов).

Общая форма объявления метода с оператором throws:


тип имя_метода(список_параметров) throws список_исключений {
    // код внутри метода
}

В фрагменте список_исключений можно указать список исключений через запятую.

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


// Метод без обработки исключения
public void createCat(){
	Toast.makeText(this, "Вы создали котёнка", Toast.LENGTH_LONG).show();
	throw new NullPointerException("Кота не существует");
}

// Щелчок кнопки
public void onClick(View v) {
	createCat();
}

Если вы запустите пример, то получите ошибку. Исправим код.


// Без изменений
public void createCat() throws NullPointerException {
	Toast.makeText(this, "Вы создали котёнка", Toast.LENGTH_LONG).show();
	throw new NullPointerException("Кота не существует");
}

// Щелчок кнопки
public void onClick(View v) {
	try {
		createCat();
	} catch (NullPointerException e) {
		// TODO: handle exception
		Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
	}
}

Мы поместили вызов метода в блок try и вызвали блок catch с нужным типом исключения. Теперь ошибки не будет.

Оператор finally

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

Ключевое слово finally создаёт блок кода, который будет выполнен после завершения блока try/catch, но перед кодом, следующим за ним. Блок будет выполнен, независимо от того, передано исключение или нет. Оператор finally не обязателен, однако каждый оператор try требует наличия либо catch, либо finally.

Существуют несколько готовых системных исключений. Большинство из них являются подклассами типа RuntimeException и их не нужно включать в список throws. Вот небольшой список непроверяемых исключений.

  • ArithmeticException — арифметическая ошибка, например, деление на нуль
  • ArrayIndexOutOfBoundsException — выход индекса за границу массива
  • ArrayStoreException — присваивание элементу массива объекта несовместимого типа
  • ClassCastException — неверное приведение
  • EnumConstantNotPresentException — попытка использования неопределённого значения перечисления
  • IllegalArgumentException — неверный аргумент при вызове метода
  • IllegalMonitorStateException — неверная операция мониторинга
  • IllegalStateException — некорректное состояние приложения
  • IllegalThreadStateException — запрашиваемая операция несовместима с текущим потоком
  • IndexOutofBoundsException — тип индекса вышел за допустимые пределы
  • NegativeArraySizeException — создан массив отрицательного размера
  • NullPointerException — неверное использование пустой ссылки
  • NumberFormatException — неверное преобразование строки в числовой формат
  • SecurityException — попытка нарушения безопасности
  • StringIndexOutOfBounds — попытка использования индекса за пределами строки
  • TypeNotPresentException — тип не найден
  • UnsupportedOperationException — обнаружена неподдерживаемая операция

Список проверяемых системных исключений, которые можно включать в список throws.

  • ClassNotFoundException — класс не найден
  • CloneNotSupportedException — попытка клонировать объект, который не реализует интерфейс Cloneable
  • IllegalAccessException — запрещен доступ к классу
  • InstantiationException — попытка создать объект абстрактного класса или интерфейса
  • InterruptedException — поток прерван другим потоком
  • NoSuchFieldException — запрашиваемое поле не существует
  • NoSuchMethodException — запрашиваемый метод не существует
  • ReflectiveOperationException — исключение, связанное с рефлексией

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

Система не может предусмотреть все исключения, иногда вам придётся создать собственный тип исключения для вашего приложения. Вам нужно наследоваться от Exception (напомню, что этот класс наследуется от Trowable) и переопределить нужные методы класса Throwable. Либо вы можете наследоваться от уже существующего типа, который наиболее близок по логике с вашим исключением.

  • final void addSuppressed(Throwable exception) — добавляет исключение в список подавляемых исключений (JDK 7)
  • Throwable fillInStackTrace() — возвращает объект класса Throwable, содержащий полную трассировку стека.
  • Throwable getCause() — возвращает исключение, лежащее под текущим исключение или null
  • String getLocalizedMessage() — возвращает локализованное описание исключения
  • String getMessage() — возвращает описание исключения
  • StackTraceElement[] getStackTrace() — возвращает массив, содержащий трассировку стека и состояний из элементов класса StackTraceElement
  • final Throwable[] getSuppressed() — получает подавленные исключения (JDK 7)
  • Throwable initCause(Throwable exception) — ассоциирует исключение с вызывающим исключением. Возвращает ссылку на исключение.
  • void printStackTrace() — отображает трассировку стека
  • void printStackTrace(PrintStream stream) — посылает трассировку стека в заданный поток
  • void printStackTrace(PrintWriter stream) — посылает трассировку стека в заданный поток
  • void setStackTrace(StackTraceElement elements[]) — устанавливает трассировку стека для элементов (для специализированных приложений)
  • String toString() — возвращает объект класса String, содержащий описание исключения.

Самый простой способ — создать класс с конструктором по умолчанию.


// Если этот код работает, его написал Александр Климов,
// а если нет, то не знаю, кто его писал.

package ru.alexanderklimov.exception;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void testMethod() throws HungryCatException{
        System.out.println("Возбуждаем HungryCatException из метода testMethod()");
        throw new HungryCatException(); // конструктор по умолчанию
    }

    public void onClick(View view) {
        try {
            testMethod();
        } catch (HungryCatException e) {
            e.printStackTrace();
            System.out.println("Наше исключение перехвачено");
        }
    }

    class HungryCatException extends Exception{
    }
}

Мы создали собственный класс HungryCatException, в методе testMethod() его возбуждаем, а по нажатию кнопки вызываем этот метод. В результате наше исключение сработает.

Создать класс исключения с конструктором, который получает аргумент-строку, также просто.


// Если этот код работает, его написал Александр Климов,
// а если нет, то не знаю, кто его писал.

package ru.alexanderklimov.exception;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void testMethod() throws HungryCatException {
        System.out.println("Возбуждаем HungryCatException из метода testMethod()");
        throw new HungryCatException(); // конструктор по умолчанию
    }

    public void testMethod2() throws HungryCatException {
        System.out.println("Возбуждаем HungryCatException из метода testMethod2()");
        throw new HungryCatException("Создано во втором методе");
    }

    public void onClick(View view) {
        try {
            testMethod();
        } catch (HungryCatException e) {
            e.printStackTrace();
            System.out.println("Наше исключение перехвачено");
        }

        try {
            testMethod2();
        } catch (HungryCatException e) {
            e.printStackTrace();
        }
    }

    class HungryCatException extends Exception {
        HungryCatException() {
        }

        HungryCatException(String msg) {
            super(msg);
        }
    }
}

Ещё вариант. Добавим также метод toString().


class CustomException extends Exception {
    String message;

    CustomException(String str) {
        message = str;
    }

    public String toString() {
        return ("Custom Exception Occurred: " + message);
    }
}

// где-то вызываем
try {
    throw new CustomException("This is a custom message");
} catch (CustomException e) {
    System.out.println(e);
}

Теперь класс содержит два конструктора. Во втором конструкторе используется конструктор родительского класса с аргументом String, вызываемый ключевым словом super.

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

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

cacth(Exception e) {
    Log.w("Log", "Перехвачено исключение");
}

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

Основные правила обработки исключений

Используйте исключения для того, чтобы:

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

  • Написать более лучше исправить ошибку
  • Написанный поспешно текст содержал множество ошибок причастный оборот где
  • Написанное сочинение незамеченная ошибка
  • Написание через дефис орфографическая ошибка
  • Написание также какая ошибка