In Java, there are three methods to print exception information. All of them are present in the Throwable class. Since Throwable is the base class for all exceptions and errors, we can use these three methods on any exception object.
Methods to Print Exceptions in Java
There are three methods to print exception messages in Java. These are:
1. java.lang.Throwable.printStackTrace() method:
By using this method, we will get the name(e.g., java.lang.ArithmeticException) and description(e.g., / by zero) of an exception separated by a colon, and the stack trace (wherein the code, that exception has occurred) in the next line.
Syntax:
public void printStackTrace()
Java
public
class
Test {
public
static
void
main(String[] args)
{
try
{
int
a =
20
/
0
;
}
catch
(Exception e) {
e.printStackTrace();
System.out.println(e);
}
}
}
Runtime Exception:
java.lang.ArithmeticException: / by zero at Test.main(Test.java:9)
Output:
java.lang.ArithmeticException: / by zero
2. toString() method:
Using this method will only get the name and description of an exception. Note that this method is overridden in the Throwable class.
Java
public
class
Test {
public
static
void
main(String[] args)
{
try
{
int
a =
20
/
0
;
}
catch
(Exception e) {
System.out.println(e.toString());
}
}
}
Output
java.lang.ArithmeticException: / by zero
3. java.lang.Throwable.getMessage() method:
Using this method, we will only get a description of an exception.
Syntax:
public String getMessage()
Java
public
class
Test {
public
static
void
main(String[] args)
{
try
{
int
a =
20
/
0
;
}
catch
(Exception e) {
System.out.println(e.getMessage());
}
}
}
This article is contributed by Gaurav Miglani. If you like GeeksforGeeks and would like to contribute, you can also write an article using write.geeksforgeeks.org or mail your article to review-team@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks. Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.
Last Updated :
06 Dec, 2021
Like Article
Save Article
Suppose in your program you might get an IndexOutOfBoundsException. i am handling it in the following way:
try{
//throws an IndexOutOfBoundsException during runtime
}catch(IndexOutOfBoundsException ex){
System.err.println(ex);
}
This will only display java.lang.IndexOutOfBoundsException. But I would like to display a detailed error message (which won’t terminate the program), like the one that java gives us (lineNumber, fileName, etc) when we do not handle the error and thus terminates the program.
asked Oct 31, 2016 at 20:38
1
In Java you can use printStackTrace
on any exception object to get the stack trace printed by Java. In your case a minimal:
try {
// Throw an IndexOutOfBoundsException
} catch (IndexOutOfBoundsException ex) {
ex.printStackTrace();
}
This prints the stack trace to System.err
. You can also pass it a print stream or even System.out
to print to that particular stream.
Additionally, if you use java logger, you can use:
logger.log(<LOG_LEVEL>, <LOG_MESSAGE>, ex);
to log the exception. For more details see: https://docs.oracle.com/javase/7/docs/api/java/util/logging/Logger.html
answered Oct 31, 2016 at 20:47
Use ex.printStackTrace()
method to print the exception:
try {
int[] x = new int[1];
x[2] = 5;
} catch (IndexOutOfBoundsException ex) {
ex.printStackTrace();
}
System.err.println("Program completed successfully");
Demo.
If you are running in an environment where console output is not desirable, call ex.getStackTrace()
, and display elements in a way that is consistent with the user interface of your program.
answered Oct 31, 2016 at 20:42
2
You can use
ex.GetMessage()
Thanks
answered Oct 31, 2016 at 20:42
2
Got this from one of my teachers. Might be helpful for other beginners like me.
(This is not a part of any h.w./assignment/etc. Curiosity.)
public class ExceptionDemo{
public static void main(String[] args){
int[] array = new int[2];
try {
System.out.println(array[100]);//non-existent
} catch (IndexOutOfBoundsException ex){
StackTraceElement[] e = ex.getStackTrace();
System.err.println("Got error= " + ex + "\n"+
"in file "+e[0].getFileName() +"\n"+
"in class "+e[0].getClassName() +"\n"+
"in method "+e[0].getMethodName() +"\n"+
"in line "+e[0].getLineNumber());
System.err.println("Full trace= ");
ex.printStackTrace(System.err);
}
System.out.println("As Salamu Alaikum");
}
}
answered Nov 1, 2016 at 9:53

The Message Box
in Java is the pop-up that appears on the screen for displaying some message and waits for confirmation from the user. The term JOptionPane
is the Java-provided class that provides users the privilege to show message dialogue boxes. This class is inherited from the JComponent
class and is present in the javax.swing
package.
Below is the code block to show how the message box in Java works.
import javax.swing.*;
public class DialogueBoxPopUp {
public static void main(String[] args) {
JOptionPane.showMessageDialog(null,
"Hi, In the message box",
"PopUp Dialog",
JOptionPane.INFORMATION_MESSAGE);
}
}
In the above simple code block, the JOptionPane
class prompts users with message boxes and waits for the response. The class has some static methods that serve as utilities for the user. The method showConfirmDialog
asks a question and confirms over options as yes, no, and cancel. The showInputDialog
method prompts the user for some input. The showMessageDialog
function tells the user about some happenings.
The block above uses an overloaded version of the showMessageDialog
method and takes four parameters. Firstly, the parentComponent
argument checks for the frame in which the component can get displayed. If the value is a null
value, then it uses the default frame. In the previous program, the null frame gets passed, so the code uses the default frame.
Next is the message
argument that takes the message Object
to be displayed. The title
argument takes the title string for the pop-up box. The message in the above block takes the title as the PopUp Dialog
that comes on the top of the dialog box.
The messageType
is the type of message that executes ERROR_MESSAGE
INFORMATION_MESSAGE
WARNING_MESSAGE
QUESTION_MESSAGE
or PLAIN_MESSAGE
values. These values are present as static and final values as the type of message in the JOptionPane
class. The code uses INFORMATION_MESSAGE
as the message type.
Check the previous program’s output here:
If the message type changes to JOptionPane.ERROR_MESSAGE
, the error message dialog is as the image below.
If the message type changes to JOptionPane.WARNING_MESSAGE
, the warning message dialog looks like below.
There are some more message types that one can use when needed.
Исключение (exception) — это ненормальная ситуация (термин «исключение» здесь следует понимать как «исключительная ситуация»), возникающая во время выполнения программного кода. Иными словами, исключение — это ошибка, возникающая во время выполнения программы (в runtime).
Исключение — это способ системы Java (в частности, JVM — виртуальной машины Java) сообщить вашей программе, что в коде произошла ошибка. К примеру, это может быть деление на ноль, попытка обратиться к массиву по несуществующему индексу, очень распространенная ошибка нулевого указателя (NullPointerException
) — когда вы обращаетесь к ссылочной переменной, у которой значение равно null и так далее.
В любом случае, с формальной точки зрения, Java не может продолжать выполнение программы.
Обработка исключений (exception handling) — название объектно-ориентированной техники, которая пытается разрешить эти ошибки.
Программа в Java может сгенерировать различные исключения, например:
-
программа может пытаться прочитать файл из диска, но файл не существует;
-
программа может попытаться записать файл на диск, но диск заполнен или не отформатирован;
-
программа может попросить пользователя ввести данные, но пользователь ввел данные неверного типа;
-
программа может попытаться осуществить деление на ноль;
-
программа может попытаться обратиться к массиву по несуществующему индексу.
Используя подсистему обработки исключений Java, можно управлять реакцией программы на появление ошибок во время выполнения. Средства обработки исключений в том или ином виде имеются практически во всех современных языках программирования. В Java подобные инструменты отличаются большей гибкостью, понятнее и удобнее в применении по сравнению с большинством других языков программирования.
Преимущество обработки исключений заключается в том, что она предусматривает автоматическую реакцию на многие ошибки, избавляя от необходимости писать вручную соответствующий код.
В Java все исключения представлены отдельными классами. Все классы исключений являются потомками класса Throwable
. Так, если в программе возникнет исключительная ситуация, будет сгенерирован объект класса, соответствующего определенному типу исключения. У класса Throwable
имеются два непосредственных подкласса: Exception
и Error
.
Исключения типа Error
относятся к ошибкам, возникающим в виртуальной машине Java, а не в прикладной программе. Контролировать такие исключения невозможно, поэтому реакция на них в приложении, как правило, не предусматривается. В связи с этим исключения данного типа не будут рассматриваться в книге.
Ошибки, связанные с работой программы, представлены отдельными подклассами, производными от класса Exception
. В частности, к этой категории относятся ошибки деления на нуль, выхода за пределы массива и обращения к файлам. Подобные ошибки следует обрабатывать в самой программе. Важным подклассом, производным от Exception
, является класс RuntimeException
, который служит для представления различных видов ошибок, часто возникающих во время выполнения программ.
Каждой исключительной ситуации поставлен в соответствие некоторый класс. Если подходящего класса не существует, то он может быть создан разработчиком.
Так как в Java ВСЁ ЯВЛЯЕТСЯ ОБЪЕКТОМ, то исключение тоже является объектом некоторого класса, который описывает исключительную ситуацию, возникающую в определенной части программного кода.
«Обработка исключений» работает следующим образом:
-
когда возникает исключительная ситуация, JVM генерирует (говорят, что JVM ВЫБРАСЫВАЕТ исключение, для описания этого процесса используется ключевое слово
throw
) объект исключения и передает его в метод, в котором произошло исключение; -
вы можете перехватить исключение (используется ключевое слово
catch
), чтобы его каким-то образом обработать. Для этого, необходимо определить специальный блок кода, который называется обработчиком исключений, этот блок будет выполнен при возникновении исключения, код должен содержать реакцию на исключительную ситуацию; -
таким образом, если возникнет ошибка, все необходимые действия по ее обработке выполнит обработчик исключений.
Если вы не предусмотрите обработчик исключений, то исключение будет перехвачено стандартным обработчиком Java. Стандартный обработчик прекратит выполнение программы и выведет сообщение об ошибке.
Рассмотрим пример исключения и реакцию стандартного обработчика Java.
public static void main(String[] args) {
System.out.println(5 / 0);
Мы видим, что стандартный обработчик вывел в консоль сообщение об ошибке. Давайте разберемся с содержимым этого сообщения:
«C:\Program Files\Java\jdk1.8.0_60\bin\java»…
Exception in thread «main» java.lang.ArithmeticException: / by zero
at ua.opu.Main.main(Main.java:6)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Process finished with exit code 1
Exception in thread «main» java.lang.ArithmeticException: / by zero
сообщает нам тип исключения, а именно класс ArithmeticException
(про классы исключений мы будем говорить позже), после чего сообщает, какая именно ошибка произошла. В нашем случае это деление на ноль.
at ua.opu.Main.main(Main.java:6)
в каком классе, методе и строке произошло исключение. Используя эту информацию, мы можем найти ту строчку кода, которая привела к исключительной ситуации, и предпринять какие-то действия. Строки
at ua.opu.Main.main(Main.java:6)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
называются «трассировкой стека» (stack tracing). О каком стеке идет речь? Речь идет о стеке вызовов (call stack). Соответственно, эти строки означают последовательность вызванных методов, начиная от метода, в котором произошло исключение, заканчивая самым первым вызванным методом.
Для вызова методов в программе используется инструкция «call». Когда вы вызываете метод в программе, важно сохранить адрес следующей инструкции, чтобы, когда вызванный метод отработал, программа продолжила работу со следующей инструкции. Этот адрес нужно где-то хранить в памяти. Также перед вызовом необходимо сохранить аргументы функции, которые тоже необходимо где-то хранить.
Вся эта информация хранится в специальной структуре – стеке вызовов. Каждая запись в стеке вызовов называется кадром или фреймом (stack frame).
Таким образом, зная, какая строка привела к возникновению исключения, вы можете изменить код либо предусмотреть обработчик событий.
Как уже было сказано выше, исключение это объект некоторого класса. В Java существует разветвленная иерархия классов исключений.
В Java, класс исключения служит для описания типа исключения. Например, класс NullPointerException
описывает исключение нулевого указателя, а FileNotFoundException
означает исключение, когда файл, с которым пытается работать приложение, не найден. Рассмотрим иерархию классов исключений:
На самом верхнем уровне расположен класс Throwable
, который является базовым для всех исключений (как мы помним, JVM «выбрасывает» исключение», поэтому класс Throwable
означает – то, что может «выбросить» JVM).
От класса Throwable
наследуются классы Error
и Exception
. Среди подклассов Exception
отдельно выделен класс RuntimeException
, который играет важную роль в иерархии исключений.
В Java существует некоторая неопределенность насчет того – существует ли два или три вида исключений.
Если делить исключения на два вида, то это:
-
1.
контролируемые исключения (checked exceptions) – подклассы класса
Exception
, кроме подклассаRuntimeException
и его производных; -
2.
неконтролируемые исключения (unchecked exceptions) – класс
Error
с подклассами, а также классRuntimeException
и его производные;
В некоторых источниках класс Error
и его подклассы выделяют в отдельный вид исключений — ошибки (errors).
Далее мы видим класс Error
. Классы этой ветки составляют вид исключений, который можно обозначить как «ошибки» (errors). Ошибки представляют собой серьезные проблемы, которые не следует пытаться обработать в собственной программе, поскольку они связаны с проблемами уровня JVM.
На самом деле, вы конечно можете предпринять некоторые действия при возникновении ошибок, например, вывести сообщение для пользователя в удобном формате, выслать трассировку стека себе на почту, чтобы понять – что вообще произошло.
Но, по факту, вы ничего не можете предпринять в вашей программе, чтобы эту ошибку исправить, и ваша программа, как правило, при возникновении такой ошибки дальше работать не может.
В качестве примеров «ошибок» можно привести: переполнение стека вызова (класс StackOverflowError
); нехватка памяти в куче (класс OutOfMemoryError
), вследствие чего JVM не может выделить память под новый объект и сборщик мусора не помогает; ошибка виртуальной машины, вследствие которой она не может работать дальше (класс VirtualMachineError
) и так далее.
Несмотря на то, что в нашей программе мы никак не можем помочь этой проблеме, и приложение не может работать дальше (ну как может работать приложение, если стек вызовов переполнен или JVM не может дальше выполнять код?!); знание природы этих ошибок поможет вам предпринять некоторые действия, чтобы избежать этих ошибок в дальнейшем. Например, ошибки типа StackOverflowError
и OutOfMemoryError
могут быть следствием вашего некорректного кода.
Например, попробуем спровоцировать ошибку StackOverflowError
public static void main(String[] args) {
public static void methodA() {
private static void methodB() {
Получим такое сообщение об ошибке
Exception in thread «main» java.lang.StackOverflowError
at com.company.Main.methodB(Main.java:14)
at com.company.Main.methodA(Main.java:10)
at com.company.Main.methodB(Main.java:14)
at com.company.Main.methodA(Main.java:10)
at com.company.Main.methodB(Main.java:14)
at com.company.Main.methodA(Main.java:10)
at com.company.Main.methodB(Main.java:14)
at com.company.Main.methodA(Main.java:10)
Ошибка OutOfMemoryError
может быть вызвана тем, что ваш код, вследствие ошибки при программировании, создает очень большое количество массивных объектов, которые очень быстро заполняют кучу и свободного места не остается.
Exception in thread «main» java.lang.OutOfMemoryError: Java heap space
at java.base/java.util.Arrays.copyOf(Arrays.java:3511)
at java.base/java.util.Arrays.copyOf(Arrays.java:3480)
at java.base/java.util.ArrayList.grow(ArrayList.java:237)
at java.base/java.util.ArrayList.grow(ArrayList.java:244)
at java.base/java.util.ArrayList.add(ArrayList.java:454)
at java.base/java.util.ArrayList.add(ArrayList.java:467)
at com.company.Main.main(Main.java:13)
Process finished with exit code 1
Ошибка VirtualMachineError
может означать, что следует переустановить библиотеки Java.
В любом случае, следует относиться к типу Error
не как к неизбежному злу и «воле богов», а просто как к сигналу к тому, что в вашем приложении что-то не так, или что-то не так с программным или аппаратным обеспечением, которое вы используете.
Класс Exception
описывает исключения, связанные непосредственно с работой программы. Такого рода исключения «решаемы» и их грамотная обработка позволит программе работать дальше в нормальном режиме.
В классе Exception описаны исключения двух видов: контролируемые исключения (checked exceptions) и неконтролируемые исключения (unchecked exceptions).
Неконтролируемые исключения содержатся в подклассе RuntimeException
и его наследниках. Контролируемые исключения содержатся в остальных подклассах Exception
.
В чем разница между контролируемыми и неконтролируемыми исключениями, мы узнаем позже, а теперь рассмотрим вопрос – а как же именно нам обрабатывать исключения?
Обработка исключений в методе может выполняться двумя способами:
-
1.
с помощью связки
try-catch
; -
2.
с помощью ключевого слова
throws
в сигнатуре метода.
Рассмотрим оба метода поподробнее:
Способ 1. Связка try-catch
Этот способ кратко можно описать следующим образом.
Код, который теоретически может вызвать исключение, записывается в блоке try{}
. Сразу за блоком try
идет блок код catch{}
, в котором содержится код, который будет выполнен в случае генерации исключения. В блоке finally{}
содержится код, который будет выполнен в любом случае – произошло ли исключение или нет.
Теперь разберемся с этим способом более подробно. Рассмотрим следующий пример – программу, которая складывает два числа, введенные пользователем из консоли
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println(«Введите первое число: «);
String firstNumber = scanner.nextLine();
System.out.println(«Введите второе число: «);
String secondNumber = scanner.nextLine();
a = Integer.parseInt(firstNumber);
b = Integer.parseInt(secondNumber);
System.out.println(«Результат: « + (a + b));
Первое, что нам нужно определить – и что является главным при работе с исключениями, КАКАЯ ИНСТРУКЦИЯ МОЖЕТ ПРИВЕСТИ К ВОЗНИКНОВЕНИЮ ИСКЛЮЧЕНИЯ?
То есть, мы должны понять – где потенциально у нас может возникнуть исключение? Понятно, что речь идет не об операции сложения и не об операции чтения данных из консоли. Потенциально опасными строчками кода здесь являются строчки
a = Integer.parseInt(firstNumber);
b = Integer.parseInt(secondNumber);
в которых происходит преобразование ввода пользователя в целое число (метод parseInt()
преобразует цифры в строке в число).
Почему здесь может возникнуть исключение? Потому что пользователь может ввести не число, а просто какой-то текст и тогда непонятно – что записывать в переменную a
или b
. И да, действительно, если пользователь введет некорректное значение, возникнет исключение в методе Integer.parseInt()
.
Итак, что мы можем сделать. «Опасный код» нужно поместить в блок try{}
Обратите внимание на синтаксис блока try
. В самом простом случае это просто ключевое слово try
, после которого идут парные фигурные скобки. Внутри этих скобок и заключается «опасный» код, который может вызвать исключение. Сразу после блока try должен идти блок catch()
.
a = Integer.parseInt(firstNumber);
b = Integer.parseInt(secondNumber);
} catch (NumberFormatException e) {
// сохранить текст ошибки в лог
System.out.println(«Одно или оба значения некорректны!»);
System.out.println(«Результат: « + (a + b));
Обратите внимание на синтаксис блока catch
. После ключевого слова, в скобках описывается аргумент с именем e
типа NumberFormatException
.
Когда произойдет исключение, то система Java прервет выполнение инструкций в блоке try
и передаст управление блоку catch
и запишет в этот аргумент объект исключения, который сгенерировала Java-машина.
То есть, как только в блоке try
возникнет исключение, то дальше инструкции в блоке try выполняться не будут! А сразу же начнут выполняться действия в блоке catch
.
Обработчик исключения находится в блоке catch
, в котором мы можем отреагировать на возникновение исключения. Также, в этом блоке нам будет доступен объект исключения, от которого мы можем получить дополнительные сведения об исключении.
Блок catch
сработает только в том случае, если указанный в скобках тип объекта исключения будет суперклассом или будет того же типа, что и объект исключения, который сгенерировала Java.
Например, если в нашем примере мы напишем код, который потенциально может выбросить исключение типа IOException
, но не изменим блок catch
} catch (NumberFormatException e) {
// сохранить текст ошибки в лог
System.out.println(«Одно или оба значения некорректны!»);
тогда обработчик не будет вызван и исключение будет обработано стандартным обработчиком Java.
Способ 2. Использование ключевого слова throws
Второй способ позволяет передать обязанность обработки исключения тому методу, который вызывает данный метод (а тот, в свою очередь может передать эту обязанность выше и т.д.).
Изменим наш пример и выделим в отдельный метод код, который будет запрашивать у пользователя число и возвращать его как результат работы метода
public static void main(String[] args) {
int a = getNumberFromConsole(«Введите первое число»);
int b = getNumberFromConsole(«Введите второе число»);
System.out.println(«Результат: « + (a + b));
public static int getNumberFromConsole(String message) {
Scanner scanner = new Scanner(System.in);
System.out.print(message + «: «);
String s = scanner.nextLine();
return Integer.parseInt(s);
Мы понимаем, что в данном методе может произойти исключение, но мы не хотим или не можем его обработать. Причины могут быть разными, например:
-
1.
обработка исключений может происходить централизованно однотипным способом (например, показ окошка с сообщением и с определенным текстом);
-
2.
это не входит в нашу компетенцию как программиста – обработкой исключений занимается другой программист;
-
3.
мы пишем только некоторую часть программы и непонятно – как будет обрабатывать исключение другой программист, который потом будет использовать наш код (например, мы пишем просто какую-то библиотеку, которая производит вычисления, и как будет выглядеть обработка – это не наше дело).
В любом случае, мы знаем, что в этом коде может быть исключение, но мы не хотим его обрабатывать, а хотим просто предупредить другой метод, который будет вызывать наш код, что выполнение кода может привести к исключению. В этом случае, используется ключевое слово throws
, которое указывается в сигнатуре метода
public static int getNumberFromConsole(String message) throws NumberFormatException {
Scanner scanner = new Scanner(System.in);
System.out.print(message + «: «);
String s = scanner.nextLine();
return Integer.parseInt(s);
Обратите внимание на расположение сигнатуру метода. Мы привыкли, что при объявлении метода сразу после скобок входных аргументов мы открываем фигурную скобку и записываем тело метода. Здесь же, после входных аргументов, мы пишем ключевое слово throws
и потом указываем тип исключения, которое может быть сгенерировано в нашем методе. Если метод может выбрасывать несколько типов исключений, они записываются через запятую
public static void foo() throws NumberFormatException, ArithmeticException, IOException {
Тогда, в методе main мы должны написать примерно следующее
public static void main(String[] args) {
a = getNumberFromConsole(«Введите первое число»);
b = getNumberFromConsole(«Введите второе число»);
} catch (NumberFormatException e) {
// сохранить текст ошибки в лог
System.out.println(«Одно или оба значения некорректны!»);
System.out.println(«Результат: « + (a + b));
Основное преимущество этого подхода – мы передаем обязанность по обработке исключений другому, вышестоящему методу.
Отличия между контролируемыми и неконтролируемыми исключениями
Отличия между контролируемыми и неконтролируемыми исключениями
Если вы вызываете метод, который выбрасывает checked исключение, то вы ОБЯЗАНЫ предусмотреть обработку возможного исключения, то есть связку try-catch
.
Яркий пример checked исключения – класс IOException
и его подклассы.
Рассмотрим пример – попробуем прочитать файл и построчно вывести его содержимое на экран консоли:
public static void main(String[] args) {
Path p = Paths.get(«c:\\temp\\file.txt»);
BufferedReader reader = Files.newBufferedReader(p);
while ((line = reader.readLine()) != null) {
System.out.println(line);
Как мы видим, компилятор не хочет компилировать наш код. Чем же он недоволен? У нас в коде происходит вызов двух методов – статического метода Files.newBufferedReader()
и обычного метода BufferedReader.readLine()
.
Если посмотреть на сигнатуры этих методов то можно увидеть, что оба этих метода выбрасывают исключения типа IOException
. Этот тип исключения относится к checked-исключению и поэтому, если вы вызываете эти методы, компилятор ТРЕБУЕТ от вас предусмотреть блок catch
, либо в самом вашем методе указать throws IOException
и, таким образом, передать обязанность обрабатывать исключение другому методу, который будет вызывать ваш.
Таким образом, «оборачиваем» наш код в блок try
и пишем блок catch
.
public static void main(String[] args) {
Path p = Paths.get(«c:\\temp\\file.txt»);
BufferedReader reader = Files.newBufferedReader(p);
while ((line = reader.readLine()) != null) {
System.out.println(line);
} catch (IOException e) {
System.out.println(«Ошибка при чтении файла!»);
Еще один способ — указать в сигнатуре метода, что он выбрасывает исключение типа IOException
и переложить обязанность обработать ошибку в вызывающем коде
public static void main(String[] args) {
Path p = Paths.get(«c:\\temp\\file.txt»);
} catch (IOException e) {
System.out.println(«Ошибка при чтении файла!»);
public static void printFile(Path p) throws IOException {
BufferedReader reader = Files.newBufferedReader(p);
while ((line = reader.readLine()) != null) {
System.out.println(line);
Eсли метод выбрасывает checked-исключение, то проверка на наличие catch-блока происходит на этапе компиляции. И вы обязаны предусмотреть обработку исключения для checked-исключения.
Что касается unchecked-исключения, то обязательной обработки исключения нет – вы можете оставить подобные ситуации без обработки.
Зачем необходимо наличие двух видов исключений?
Зачем необходимо наличие двух видов исключений?
В большинстве языков существует всего лишь один тип исключений – unchecked. Некоторые языки, например, C#, в свое время отказались от checked-исключений.
Во-первых, мы не можем сделать все исключения checked, т.к. очень многие операции могут генерировать исключения, и если каждый такой участок кода «оборачивать» в блок try-catch
, то код получится слишком громоздким и нечитабельным.
С другой стороны, зачем нужно делать некоторые типы исключений checked? Почему просто не сделать все исключения unchecked и оставить решения об обработке исключений целиком на совести программиста?
В официальной документации написано, что unchecked-исключения – это те исключения, от которых программа «не может восстановиться», тогда как checked-исключения позволяют откатить некоторую операцию и повторить ее снова.
На самом деле, если вы посмотрите на различные типы unchecked-исключений, то вы увидите, что большинство их связаны с ошибками самого программиста. Выход за пределы массива, исключение нулевого указателя, деление на ноль – большинство из подобного рода исключений целиком лежат на совести программистов. Тогда мы можем сказать, что лучше программист пишет более хороший код, чем везде вставляет проверки на исключения.
Контролируемые исключения, как правило, представляют те ошибки, которые возникают не из-за программиста и предусмотреть которые программист не может. Например, это отсутствующие файлы, работа с сокетами, подключение к базе данных, сетевые соединения, некорректный пользовательский ввод.
Вы можете написать идеальный код, но потом вы отдадите приложение пользователю, а он введет название файла, которого нет или напишет неправильный IP для сокет-соединения. Таким образом, мы заранее должны быть готовыми к неверным действиям пользователя или к программным или аппаратным проблемам на его стороне и в обязательном порядке предусмотреть обработку возможных исключений.
Дополнительно об исключениях
Дополнительно об исключениях
Рассмотрим детально различные возможности механизма исключений, которые позволяют программисту максимально эффективно противодействовать исключениям:
Java позволяет вам для одного блока try
предусмотреть несколько блоков catch
, каждый из которых должен обрабатывать свой тип исключения
public static void foo() {
} catch (ArithmeticException e) {
// обработка арифметического исключения
} catch (IndexOutOfBoundsException e) {
// обработка выхода за пределы коллекции
} catch (IllegalArgumentException e) {
// обработка некорректного аргумента
Важно помнить, что Java обрабатывает исключения последовательно. Java просматривает блок catch сверху вниз и выполняет первый подходящий блок, который может обработать данное исключение.
Так как вы можете указать как точный класс, так и суперкласс, то если первым блоком будет блок для суперкласса – выполнится он. Например, исключение FileNotFoundException
является подклассом IOException
. И поэтому если вы первым поставите блок с IOException
– он будет вызываться для всех подтипов исключений, в том числе и для FileNotFoundException
и блок c FileNotFoundException
никогда не выполнится.
public static void main(String[] args) {
Path p = Paths.get(«c:\\temp\\file.txt»);
} catch (IOException e) {
System.out.println(«Ошибка при чтении файла!»);
} catch (FileNotFoundException e) {
// данный блок никогда не будет вызван
public static void printFile(Path p) throws IOException {
BufferedReader reader = Files.newBufferedReader(p);
while ((line = reader.readLine()) != null) {
System.out.println(line);
Один блок для обработки нескольких типов исключений
Один блок для обработки нескольких типов исключений
Начиная с версии Java 7, вы можете использовать один блок catch
для обработки исключений нескольких, не связанных друг с другом типов. Приведем пример
public static void foo() {
} catch (ArithmeticException | IllegalArgumentException | IndexOutOfBoundsException e) {
// три типа исключений обрабатываются одинаково
Как мы видим, один блок catch используется для обработки и типа IOException
и NullPointerException
и NumberFormaException
.
Вы можете использовать вложенные блоки try
, которые могут помещаться в других блоках try
. После вложенного блока try
обязательно идет блок catch
public static void foo() {
} catch (IllegalArgumentException e) {
// обработка вложенного блока try
} catch (ArithmeticException e) {
Выбрасывание исключения с помощью ключевого слова throw
С помощью ключевого слова throw
вы можете преднамеренно «выбросить» определенный тип исключения.
public static void foo(int a) {
throw new IllegalArgumentException(«Аргумент не может быть отрицательным!»);
Кроме блока try
и catch
существует специальный блок finally
. Его отличительная особенность – он гарантированно отработает, вне зависимости от того, будет выброшено исключение в блоке try
или нет. Как правило, блок finally
используется для того, чтобы выполнить некоторые «завершающие» операции, которые могли быть инициированы в блоке try
.
public static void foo(int a) {
FileOutputStream fout = null;
File file = new File(«file.txt»);
fout = new FileOutputStream(file);
} catch (IOException e) {
// обработка исключения при записи в файл
} catch (IOException e) {
При любом развитии события в блоке try
, код в блоке finally
отработает в любом случае.
Блок finally
отработает, даже если в try-catch
присутствует оператор return
.
Как правило, блок finally
используется, когда мы в блоке try
работаем с ресурсами (файлы, базы данных, сокеты и т.д.), когда по окончании блока try-catch
мы освобождаем ресурсы. Например, допустим, в процессе работы программы возникло исключение, требующее ее преждевременного закрытия. Но в программе открыт файл или установлено сетевое соединение, а, следовательно, файл нужно закрыть, а соединение – разорвать. Для этого удобно использовать блок finally
.
Блок try-with-resources
является модификацией блока try
. Данный блок позволяет автоматически закрывать ресурс после окончания работы блока try
и является удобной альтернативой блоку finally
.
public static void foo() {
Path p = Paths.get(«c:\\temp\\file.txt»);
try (BufferedReader reader = Files.newBufferedReader(p)) {
while ((line = reader.readLine()) != null)
System.out.println(line);
} catch (IOException e) {
Внутри скобок блока try
объявляется один или несколько ресурсов, которые после отработки блока try-catch
будут автоматически освобождены. Для этого объект ресурса должен реализовывать интерфейс java.lang.AutoCloseable
.
Создание собственных подклассов исключений
Создание собственных подклассов исключений
Встроенные в Java исключения позволяют обрабатывать большинство распространенных ошибок. Тем не менее, вы можете создавать и обрабатывать собственные типы исключений. Для того, чтобы создать класс собственного исключения, достаточно определить как его произвольный от Exception
или от RuntimeException
(в зависимости от того, хотите ли вы использовать checked или unchecked – исключения).
Насчет создания рекомендуется придерживаться двух правил:
-
1.
определитесь, исключения какого типа вы хотите использовать для собственных исключений (checked или unchecked) и старайтесь создавать исключения только этого типа;
-
2.
старайтесь максимально использовать стандартные типы исключений и создавать свои типы только в том случае, если существующие типы исключений не отражают суть того исключения, которое вы хотите добавить.
Плохие практики при обработке исключений
Плохие практики при обработке исключений
Ниже представлены действия по обработке ошибок, которые характерны для плохого программиста. Ни в коем случае не рекомендуется их повторять!
-
1.
Указание в блоке catch объекта исключения типа Exception. Существует очень большой соблазн при создании блока
catch
указать тип исключенияException
и, таким образом, перехватывать все исключения, которые относятся к этому классу (а это все исключения, кроме системных ошибок). Делать так крайне не рекомендуется, т.к. вместо того чтобы решать проблему с исключениями, мы фактически игнорируем ее и просто реализуем некоторую «заглушку», чтобы приложение продолжило работу дальше. Кроме того, каждый тип исключения должен быть обработан своим определенным образом. -
2.
Помещение в блок
try
всего тела метода. Следующий плохой прием используется, когда программист не хочет разбираться с кодом, который вызывает исключение и просто, опять же, реализует «заглушку». Этот прием очень «хорошо» сочетается с первым приемом. В блокtry
должен помещаться только тот код, который потенциально может вызвать исключение, а не всё подряд, т.к. лень обрабатывать исключения нормально. -
3.
Игнорирование исключения. Следующий плохой прием состоит в том, что мы просто игнорируем исключение и оставляем блок
catch
пустым. Программа должна реагировать на исключения и должна информировать пользователя и разработчика о том, что что-то пошло не так. Безусловно, исключение это не повод тут же закрывать приложение, а попытаться повторить то действие, которое привело к исключению (например, повторно указать название файла, попытаться открыть базу данных через время и т.д.). В любом случае, когда приложение в ответ на ошибку никак не реагирует – не выдает сообщение, но и не делает того, чего от нее ожидали – это самый плохой вариант.
Исключения
Причиной появления исключений, на мой взгляд, стало очень простое предположение — во время исполнения программы вряд ли можно быть уверенным, что все функции будут делать свою работу без сбоев. Например, если ваш код должен прочитать файл с каким-то именем, а файла такого просто нет ? Или вы хотите по сети обратиться к другой программе, но добрая старушка-уборщица выдернула шнур из компьютера ? В конце концов ваш калькулятор при вычислении дроби получил в знаменателе ноль. Да много ли каких еще препятствий может встретиться на пути.
Конечно, не все методы/функции обязательно будут оканчиваться крахом, но это обычное дело. И что в этом случае необходимо предпринять ? В старое доброе время в языке Си было принято возвращать какое-то значение. Причем в разных случаях тот же ноль мог означать что все хорошо, но мог означать что все плохо. В общем стандарта как не было, так до сих пор и нет — что должна возвращать функция в случае возникновения ошибки. Определенно можно сказать только одно — функция должна возвращать:
а) признак того, что произошла ошибка
б) информацию о том, а что собственно плохого произошло
С пунктом б) как раз достаточно просто — у нас же ООП. Значит удобно всю необходимую информацию поместить в какой-либо объект какого-то класса и вернуть. В этом объекте мы можем развернуться по полной — и сообщение написать и код ошибки выдать и много чего еще.
Что касается пункта а), то несложно догадаться, что возвращать ошибку как результат выполнения функции — плохая идея. Если мы ожидаем целое число, а ошибка будет описываться пусть даже просто строкой, то вряд ли кто-то сможет предложить что-то более-менее приемлемое. Но идея возвращать объект-описание ошибки — красивая и благодарная идея.
Отсюда родилась следующее решение — метод может генерировать исключение (объект-ошибку) и завершаться не вызовом return в случае ошибки, а иначе — метод будет «бросать» исключение.
И тогда код, вызывающий такой метод может получить ответ двумя способами:
- Обычным возвратом значения из метода, если все хорошо
- Будет «ловить» исключение
Соответственно метод, который хочет возвращать ошибку, тоже должен уметь возвращать какое-то значение (или ничего не возвращать в случае void) когда все хорошо и создавать и «бросать» исключения, если что-то пошло не так. Я уже дважды использовал термин «бросать», причем не без умысла — в языке Java используется глагол throw (бросать, кидать), посему больше не буду брать его в кавычки.
Мы получаем три момента для рассмотрения:
- Описание объекта-исключения — его же надо уметь создавать
- Описание метода, который умеет бросать исключения
- Как правильно ловить исключение
Итак, еще раз — надо создать исключение, надо правильно бросить исключение, надо правильно поймать исключение.
Класс для исключения
Научимся создавать исключения. Для таких классов существует специальная иерархия классов, которая начинается с класса java.lang.Throwable. (Надеюсь вы помните, что java.lang означет пакет, в котором находится описание класса. По сути директория). Если перевести это название получится что-то вроде «готовый к бросанию» — с литературным переводом возможно у меня не очень красиво получилось, но идея именно такая: объекты этого класса (и всех потомков) могут быть брошены. Этот класс редко используется для прямого наследования, чаще используется его потомок, класс java.lang.Exception. А уж у этого класса «детишек» очень много. Как вы наверно уже догадались, количество готовых классов для Java огромно. Вряд ли конечно на каждого человека на земле приходится один класс, хотя возможно я не так уж и далек от истины. В общем, уже готовых классов-исключений среди других много. Так что будет что изучать. Но о них мы поговорим несколько позже, а пока все-таки создадим наш класс-исключение.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
package edu.javacourse.exception; public class SimpleException extends Exception { // Это наше поле для хранения информации, присущей данному // классу-исключению. Поле немножко надуманное, но здесь может быть // и достаточно важная информация private int errorCode; // переопределяем конструктор public SimpleException(String message) { this(0, message); } // Создаем свой конструктор public SimpleException(int errorCode, String message) { // Вызываем конструктор предка super(message); // Добавляем инициализацию своего поля this.errorCode = errorCode; } // Метод для получения кода ошибки public int getErrorCode() { return errorCode; } } |
Как вы можете видеть ничего особенного в описании нет — создали класс, унаследовали его от класса Exception, определили свой конструктор, переопределили уже существующий — в общем ничего неординарного и загадочного. Единственное, что хотелось бы отметить — это наличие у класса Throwable (предка Exception) нескольких достаточно востребованных методов.
getMessage() — получить сообщение об ошибке, которое обычно имеет смысл читать
printStackTrace() — распечатать полный стек вызовов. Стек вызовов — это полный список всех методов внутри которых случилась ошибка. Т.е. если вызывался method1, внутри него method2, потом method3 и в нем случилось исключение, то вы увидите все три метода. Чуть позже мы с вами посмотрим пример использования этого метода. Не пренебрегайте им — он очень удобный и информативный
Метод для генерации исключения
Генерировать исключение можно в методе и этот метод может объявить, что он кидает исключения определенного класса. Делается это достаточно просто — после списка аргументов (в скобках) пишется ключевое слово throws и через запятую перечисляются классы исключений, который может порождать данный метод. Потом открывается фигурная скобка и мы пишем тело метода. Давайте посмотрим пример такого описания — наш класс Generator включает метод helloMessage который принимает в качестве строки имя, чтобы отдать строку «Hello, <имя>«. Но если имя не указано (указатель на строку равен null), то метод не возвращает например пустую строку — он кидает исключение, которое можно использовать в дальнейшей логике.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package edu.javacourse.exception; public class Generator { // Данный метод описан с указанием того, что он способен кинуть // исключение типа SimpleException public String helloMessage(String name) throws SimpleException { if (name == null) { // Мы должны сначала создать объект-исключение SimpleException se = new SimpleException(10, «Message is null»); // Теперь мы можем «кинуть» это исключение — это другой способ выйти // из метода — отличный от варианта с return throw se; // Можно совместить создание и кидание — можете закомментировать // предыдущие строки и использовать нижеприведенную // throw new SimpleException(10, «Message is null»); } return «Hello, « + name; } } |
Опять смотрим код и видим, что после проверки на null мы создаем объект-исключение (обратите внимание — мы просто создаем нужный нам объект, заполняем его нужными значениями (мы в нем описываем реальную проблему, которая произошла) и кидаем. Опять же обращаю ваше внимание на то, что мы не делаем вызов return — этот вызов делается в случае если все хорошо. Мы кидаем исключение — пишем специальную конструкцию throw (не перепутайте — в описании метода пишем глагол в третьем лице единственного числа по английской грамматике с окончанием s — throws — «бросает», а в коде используем повелительное наклонение — throw — «бросай»).
Несколько позже мы рассмотрим еще несколько моментов, которые касаются описания методов, которые бросают исключения. Пока же перейдем к третьему шагу — как поймать исключение.
Ловим исключение
Для того, чтобы поймать исключение используется конструкция try … catch. Перед блоком кода, который порождает исключение пишем слово try и открываем фигурные скобки. После окончания блока закрываем скобки, пишем слово catch, в скобках указываем переменную класса-исключения, открываем скобки и там пишем код, который будет вызываться ТОЛЬКО в случае если метод/методы внутри try породили исключение. Смотрим код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
package edu.javacourse.exception; public class Starter { public static void main(String[] args) { // создаем наш класс для генерации исключений Generator generator = new Generator(); // Данный блок будет обрабатывать исключение // и оно там действительно возникнет — мы же передали null try { String answer = generator.helloMessage(null); System.out.println(«Answer 1:» + answer); } catch (SimpleException ex) { // Здесь мы можем обработать объект-исключение, // получить некоторую информаицию System.out.println(«Error code:» + ex.getErrorCode()); System.out.println(«Error message:» + ex.getMessage()); } // Данный блок будет обрабатывать исключение // но его не будет — мы передали корректный параметр try { String answer = generator.helloMessage(«Yoda»); System.out.println(«Answer 2:» + answer); } catch (SimpleException ex) { // Здесь мы можем обработать объект-исключение, // получить некоторую информаицию System.out.println(«Error:» + ex.getMessage()); } } } |
Полный текст проекта можно скачать тут — SimpleException. При запуске можно увидеть, что первый кусочек try … catch выкинет исключение и мы увидим текст об ошибках
Error code:10
Error message:Message is null
Второй же пройдет гладко и мы увидим приветствие для самого известного джедая
Answer 2:Hello, Yoda
Обратите внимание, что в первом блоке try … catch строка «Answer 1:» не выводится. Т.е. поведение при генерации исключения следующее: сразу после создания исключения все остальные строки внутри блока уже не выполняются и вы сразу перемещаетесь в блок catch. Думаю, что это достаточно очевидно.
Итак, мы рассмотрели простой вариант использования исключений, но это конечно же не все. Так что продолжим.
Блок finally
Как мы видели чуть выше, если во время исполнения внутри блока try … catch случается исключение, то все оставшиеся строки НЕ ВЫПОЛНЯЮТСЯ. Но это не всегда полностью соответствует нашим желаниям. Существует немалое количество ситуаций, когда, чтобы не случилось, какие-то строки кода должны выполняться вне зависимости от того, каков результат. Именно для этого существует секция finally.
Например, вы открыли файл на запись и долгое время что-то туда записывали. Но наступил момент, когда какие-то данные по определенным причинам туда не попали и запиcь надо прекращать. Но даже в этой ситуации файл нужно сохранить (закрыть). Или вы открыли сетевое соединение, которое в любом случае надо закрыть. Наконец вам просто надо всегда обнулить какой-то счетчик в конце. Давайте посмотрим структуру с finally
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
package edu.javacourse.exception; public class Starter { public static void main(String[] args) { // создаем наш класс для генерации исключений Generator generator = new Generator(); // Данный блок будет обрабатывать исключение // и оно там действительно возникнет — мы же передали null try { String answer = generator.helloMessage(null); System.out.println(«Answer 1:» + answer); } catch (SimpleException ex) { // Здесь мы можем обработать объект-исключение, // получить некоторую информаицию System.out.println(«Error code:» + ex.getErrorCode()); System.out.println(«Error message:» + ex.getMessage()); } finally { // Этот блок будет вызываться всегда, независимо от результата System.out.println(«Этот блок вызываетя всегда»); } // Данный блок будет обрабатывать исключение // но его не будет — мы передали корректный параметр try { String answer = generator.helloMessage(«Yoda»); System.out.println(«Answer 2:» + answer); } catch (SimpleException ex) { // Здесь мы можем обработать объект-исключение, // получить некоторую информаицию System.out.println(«Error:» + ex.getMessage()); } finally { // Этот блок будет вызываться всегда, независиом от результата System.out.println(«Этот блок вызываетя всегда»); } } } |
Если вы запустите наш пример, то сможете увидеть, что вывод на печать в секции finally вызывается в обоих случаях.
Множество исключений
Как вы наверно уже догадываетесь, метод может порождать не одно исключение, а много. Тем более, если у вас внутри блока try … catch вызывается несколько методов, каждый из которых порождает свой вид исключений. Соответственно и «ловить» приходится несколько исключений.
try { .... } catch(Exception1 ex) { // Обработка исключения класса Exception1 } catch(Exception2 ex) { // Обработка исключения класса Exception2 } catch(Exception3 ex) { // Обработка исключения класса Exception3 } |
Рассмотрим несложный пример — класс Generator в методе helloMessage порождает два исключения, а класс Starter будет ловить эти два исключения.
package edu.javacourse.exception; public class Generator { public String helloMessage(String name) throws FirstException, SecondException { if («FIRST».equals(name)) { throw new FirstException(«FirstException occured»); } if(«SECOND».equals(name)) { throw new SecondException(«SecondException occured»); } return «Hello, « + name; } } |
Как видим в описании метода мы через запятую перечисляются все исключения, которые порождаются этим методом.
В примере ниже можно видеть нашу «многоярусную» конструкцию для обработки разных исключений в разных ветках.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package edu.javacourse.exception; public class Starter { public static void main(String[] args) { Generator generator = new Generator(); try { String answer = generator.helloMessage(«FIRST»); //String answer = generator.helloMessage(«SECOND»); //String answer = generator.helloMessage(«OTHER»); System.out.println(«Answer 1:» + answer); } catch (FirstException ex) { System.out.println(«Error message:» + ex.getMessage()); } catch (SecondException ex) { System.out.println(«Error message:» + ex.getMessage()); } } } |
Ниже код для двух классов исключений.
package edu.javacourse.exception; public class FirstException extends Exception { public FirstException(String message) { super(message); } } |
package edu.javacourse.exception; public class SecondException extends Exception { public SecondException(String message) { super(message); } } |
Как видим ничего особенно сложного нет. В Java 1.7 появилась более компактная конструкция, в которой классы исключений перечисляются в одном catch через знак «|». Вот так:
catch (FirstException | SecondException ex) {
System.out.println(«Error message:» + ex.getMessage());
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package edu.javacourse.exception; public class Starter { public static void main(String[] args) { Generator generator = new Generator(); try { String answer = generator.helloMessage(«FIRST»); //String answer = generator.helloMessage(«SECOND»); //String answer = generator.helloMessage(«OTHER»); System.out.println(«Answer 1:» + answer); } catch (FirstException | SecondException ex) { System.out.println(«Error message:» + ex.getMessage()); } } } |
Но нередко все исключения можно обработать в одной ветке catch. В этом случае можно использовать полиморфизм и наследование исключений. Как и любой класс, исключение тоже наследуется. При построении секции catch это можно использовать. Принцип следующий — поиск подходящего исключения начинается с первого catch. Вы понимаете, что все наследники класса Exception подходят под этот класс. Значит если у нас самый первый catch будет ловить класс Exception, то туда будут попадать практически все исключения.
Давайте рассмотрим вот такой пример обработки:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package edu.javacourse.exception; public class Starter { public static void main(String[] args) { Generator generator = new Generator(); try { String answer = generator.helloMessage(«FIRST»); //String answer = generator.helloMessage(«SECOND»); //String answer = generator.helloMessage(«OTHER»); System.out.println(«Answer 1:» + answer); } catch (Exception ex) { System.out.println(«Error message:» + ex.getMessage()); } catch (FirstException ex) { System.out.println(«Error message:» + ex.getMessage()); } catch (SecondException ex) { System.out.println(«Error message:» + ex.getMessage()); } } } |
Этот пример не будет компилироваться, т.к. второй и третий catch не будет достигнут никогда — мы все время попадаем в первую ветку catch. Но если мы переместим обработку Exception в конец, то этот код будет корректным, т.к. сначала мы проверяем более точные классы исключений. Код ниже на первый взгляд, не сильно отличается. но его можно скомпилировать.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package edu.javacourse.exception; public class Starter { public static void main(String[] args) { Generator generator = new Generator(); try { String answer = generator.helloMessage(«FIRST»); //String answer = generator.helloMessage(«SECOND»); //String answer = generator.helloMessage(«OTHER»); System.out.println(«Answer 1:» + answer); } catch (FirstException ex) { System.out.println(«Error message:» + ex.getMessage()); } catch (SecondException ex) { System.out.println(«Error message:» + ex.getMessage()); } catch (Exception ex) { System.out.println(«Error message:» + ex.getMessage()); } } } |
Этим приемом нередко пользуется — учитесь строить, читать и распознавать такие конструкции.
Класс RuntimeException
В Java не все исключения необходимо обрабатывать. Существует целый класс, от которого можно порождать исключения и которые не требуют обязательной обработки. Это класс RuntimeException.
Это достаточно обширная иерархия, в которой достаточно много распространенных классов — NullPointerException, ClassCastException.
Пожелания
Самое главное пожелание — не оставляйте блоки catch без информации. Очень неплохим подспорьем будет вызов метода printStackTrace. Очень важно понимать. что случилось, почему. И если блок catch не будет информативным, то выяснение причин будет крайне сложным занятием.
Ужасным кодом будет нечто вроде этого:
try { ... } catch(Exception ex) { System.out.println(«Произошло исключение»); } |
Что здесь можно понять в случае возникновения исключения ? НИЧЕГО ? Уважайте свой труд — выводите подробную информацию.
Что дальше ?
Рассмотрев исключения, мы завершили знакомство с конструкциями языка Java. Я говорю именно о конструкциях — у нас еще впереди долгий путь изучения. Но если выражаться образно, то алфавит освоили. Мы еще будем встречать некоторые конструкции и слова, но подавляющая часть уже пройдена. Впереди нас ждет изучение уже готовых классов, которые предназначены для решения различных задач.
Все конструкции служат главному — позволить вам создавать описания классов, создавать объекты и управлять последовательностью вызовов для решения своей задачи. Можно по-разному оценивать удобство, но раз это так сделано разработчиками Java — надо уметь этим пользоваться.
И еще раз выскажу пожелание — учитесь читать код, учитесь понимать программу, просто просматривая что она делает, шаг за шагом.
И теперь нас ждет следующая статья: Решения на основе классов.