Throwing unchecked exceptions java

Unchecked Exceptions — The Controversy

Because the Java programming language does not require methods to catch or to specify unchecked exceptions ( RuntimeException , Error , and their subclasses), programmers may be tempted to write code that throws only unchecked exceptions or to make all their exception subclasses inherit from RuntimeException . Both of these shortcuts allow programmers to write code without bothering with compiler errors and without bothering to specify or to catch any exceptions. Although this may seem convenient to the programmer, it sidesteps the intent of the catch or specify requirement and can cause problems for others using your classes.

Why did the designers decide to force a method to specify all uncaught checked exceptions that can be thrown within its scope? Any Exception that can be thrown by a method is part of the method’s public programming interface. Those who call a method must know about the exceptions that a method can throw so that they can decide what to do about them. These exceptions are as much a part of that method’s programming interface as its parameters and return value.

The next question might be: «If it’s so good to document a method’s API, including the exceptions it can throw, why not specify runtime exceptions too?» Runtime exceptions represent problems that are the result of a programming problem, and as such, the API client code cannot reasonably be expected to recover from them or to handle them in any way. Such problems include arithmetic exceptions, such as dividing by zero; pointer exceptions, such as trying to access an object through a null reference; and indexing exceptions, such as attempting to access an array element through an index that is too large or too small.

Runtime exceptions can occur anywhere in a program, and in a typical one they can be very numerous. Having to add runtime exceptions in every method declaration would reduce a program’s clarity. Thus, the compiler does not require that you catch or specify runtime exceptions (although you can).

One case where it is common practice to throw a RuntimeException is when the user calls a method incorrectly. For example, a method can check if one of its arguments is incorrectly null . If an argument is null , the method might throw a NullPointerException , which is an unchecked exception.

Generally speaking, do not throw a RuntimeException or create a subclass of RuntimeException simply because you don’t want to be bothered with specifying the exceptions your methods can throw.

Here’s the bottom line guideline: If a client can reasonably be expected to recover from an exception, make it a checked exception. If a client cannot do anything to recover from the exception, make it an unchecked exception.

Источник

Исключения в Java, Часть II (checked/unchecked)

Это вторая часть статьи (первая часть — try-catch-finally), посвященной такому языковому механизму Java как исключения. Она имеет вводный характер и рассчитана на начинающих разработчиков или тех, кто только приступает к изучению языка.

Также я веду курс «Scala for Java Developers» на платформе для онлайн-образования udemy.com (аналог Coursera/EdX).

1. «Магия» checked/unchecked

Механизм исключительных ситуация в Java связан с двумя элементами «магии», т.е. поведения, которое никак не отражено в исходном коде:
1. «Магию» java.lang.Throwable — в throw, catch и throws могут стоять исключительно Throwable или его наследники (мы уже разбирали в предыдущей лекции). Это «право» находиться в throw, catch и throws никак не отражено в исходном коде.
2. Все исключительные ситуации делятся на «проверяемые» (checked) и «непроверяемые» (unchecked). Это свойство присуще «корневищу» (Throwable, Error, Exception, RuntimeException) и передается по наследству. Никак не видимо в исходном коде класса исключения.

Читайте также:  tag */ echo "$title

В дальнейших примера просто учтите, что
— Throwable и Exception и все их наследники (за исключением наследников Error-а и RuntimeException-а) — checked
— Error и RuntimeException и все их наследники — unchecked

checked exception = проверяемое исключение, проверяемое компилятором.
Что именно проверяет компилятор мы разберем на этой лекции.

Напомним иерархию исключений

 Object | Throwable / \ Error Exception | RuntimeException 

Расставим значение свойства checked/unchecked

 Object | Throwable(CHECKED) / \ Error(UNCHECKED) Exception(CHECKED) | RuntimeException(UNCHECKED) 

2. Пессимистичный механизм

Я называю связь между проверяемыми исключениями и throws — «пессимистичной», польку мы можем «напугать» о большем, чем может произойти на самом деле, но не наоборот

Мы не можем бросать, но не предупредить

public class App < public static void main(String[] args) < throw new Exception(); // тут ошибка компиляции >> >> COMPILATION ERROR: unhandled exception: java.lang.Exception 

Мы не можем бросать, но предупредить о «меньшем»

import java.io.IOException; public class App < public static void main(String[] args) throws IOException < throw new Exception(); // тут ошибка компиляции >> >> COMPILATION ERROR: unhandled exception: java.lang.Exception 

Мы можем предупредить точно о том, что бросаем

Мы можем предупредить о большем, чем мы бросаем

Можем даже предупредить о том, чего вообще нет

Даже если предупреждаем о том, чего нет — все обязаны бояться

public class App < public static void main(String[] args) < f(); // тут ошибка компиляции >public static void f() throws Exception < >> >> COMPILATION ERROR: unhandled exception: java.lang.Exception 

Хотя они (испугавшиеся) могут перепугать остальных еще больше

public class App < // они пугают целым Throwable public static void main(String[] args) throws Throwable < f(); >// хотя мы пугали всего-лишь Exception public static void f() throws Exception < >> 

В чем цель «пессимистичности»? Все достаточно просто.
Вы в режиме протипирования «набросали», скажем, класс-утилиту для скачивания из интернета

public class InternetDownloader < public static byte[] (String url) throws IOException < return "Nothing! It's stub!".getBytes(); > > 

и хотели бы «принудить» пользователей вашего класса УЖЕ ОБРАБАТЫВАТЬ возможное исключение IOException, хотя из реализации-пустышки вы ПОКА НЕ ГЕНЕРИРУЕТЕ такое исключение. Но в будущем — собираетесь.

3. throws с непроверяемым (unckecked) исключением

public class App < public static void main(String[] args) < f(); >public static void f() throws RuntimeException < >> 

Эта конструкция служит цели «указать» программисту-читателю кода на то, что ваш метод может выбросить некоторое непроверяемое (unchecked) исключение.

Пример (java.lang.NumberFormatException — непроверяемое исключение):

package java.lang; public final class Integer extends Number implements Comparable  < . /** * . * * @param s a containing the * representation to be parsed * @return the integer value represented by the argument in decimal. * @exception NumberFormatException if the string does not contain a * parsable integer. */ public static int parseInt(String s) throws NumberFormatException < return parseInt(s,10); >. > 

Integer.parseInt() может бросить unchecked NumberFormatException на неподходящем аргументе (int k = Integer.parseInt(«123abc»)), это отразили
— в сигнатуре метода: throws NumberFormatException
— в документации (javadoc): @ exception
но это ни к чему нас не обязывает.

4. Множественные исключения

Рассмотрим ситуацию с кодом, который может бросать проверяемые исключения разных типов.
Далее учитывайте, что EOFException и FileNotFoundException — потомки IOException.

Мы можем точно указать, что выбрасываем

import java.io.EOFException; import java.io.FileNotFoundException; public class App < // пугаем ОБОИМИ исключениями public static void main(String[] args) throws EOFException, FileNotFoundException < if (System.currentTimeMillis() % 2 == 0) < throw new EOFException(); >else < throw new FileNotFoundException(); >> > 
import java.io.EOFException; import java.io.FileNotFoundException; public class App < // пугаем ОБОИМИ исключениями public static void main(String[] args) throws EOFException, FileNotFoundException < f0(); f1(); >public static void f0() throws EOFException public static void f1() throws FileNotFoundException > 

А можем «испугать» сильнее (предком обоих исключений)

import java.io.EOFException; import java.io.FileNotFoundException; import java.io.IOException; public class App < // пугаем ПРЕДКОМ исключений public static void main(String[] args) throws IOException < if (System.currentTimeMillis() % 2 == 0) < throw new EOFException(); >else < throw new FileNotFoundException(); >> > 
import java.io.EOFException; import java.io.FileNotFoundException; public class App < // пугаем ПРЕДКОМ исключений public static void main(String[] args) throws IOException < f0(); f1(); >public static void f0() throws EOFException public static void f1() throws FileNotFoundException > 

Можно и вот так: EOFException и FileNotFoundException «обобщаем до» IOException, а InterruptedException «пропускаем нетронутым» (InterruptedException — НЕ потомок IOException)

import java.io.EOFException; import java.io.FileNotFoundException; public class App < public static void main(String[] args) throws IOException, InterruptedException < f0(); f1(); f2(); >public static void f0() throws EOFException public static void f1() throws FileNotFoundException public static void f2() throws InterruptedException > 

5. Или catch, или throws

public class App < public static void main(String[] args) < try < throw new Exception(); >catch (Exception e) < // . >> > 

или так (ставим catch по предку и точно перехватываем)

public class App < public static void main(String[] args) < try < throw new Exception(); >catch (Throwable e) < // . >> > 

Но если перехватили только потомка, то не выйдет

public class App < public static void main(String[] args) < try < throw new Throwable(); >catch (Exception e) < // . >> > >> COMPILATION ERROR: unhandled exception: java.lang.Throwable 

Не годится, естественно, и перехватывание «брата»

public class App < public static void main(String[] args) < try < throw new Exception(); >catch (Error e) < // . >> > >> COMPILATION ERROR: unhandled exception: java.lang.Exception 

Если вы часть перехватили, то можете этим не пугать

import java.io.EOFException; import java.io.FileNotFoundException; public class App < // EOFException перехватили catch-ом, им не пугаем public static void main(String[] args) throws FileNotFoundException < try < if (System.currentTimeMillis() % 2 == 0) < throw new EOFException(); >else < throw new FileNotFoundException(); >> catch (EOFException e) < // . >> > 

6. Поведение компилятора/JVM

Необходимо понимать, что
проверка на cheched исключения происходит в момент компиляции (compile-time checking)
перехват исключений (catch) происходит в момент выполнения (runtime checking)

public class App < // пугаем Exception public static void main(String[] args) throws Exception < Throwable t = new Exception(); // и лететь будет Exception throw t; // но тут ошибка компиляции >> >> COMPILATION ERROR: unhandled exception: java.lang.Throwable 
public class App < public static void main(String[] args) throws Exception < Object ref = "Hello!"; // ref указывает на строку char c = ref.charAt(0); // но тут ошибка компиляции >> >> COMPILATION ERROR: Cannot resolve method 'charAt(int)' 

Хотя ССЫЛКА ref УКАЗЫВАЕТ на объект типа java.lang.String (у которого имеется метод charAt(int)), но ТИП ССЫЛКИ — java.lang.Object (ссылка типа java.lang.Object на объект типа java.lang.String). Компилятор ориентируется на «левый тип» (тип ссылки, а не тип ссылаемого) и не пропускает такой код.

Читайте также:  Меняем цвет шрифта при помощи HTML

Хотя В ДАННОЙ СИТУАЦИИ компилятор мог бы разобрать, что t ссылается на Exception, а ref — на String, но этого уже невозможно сделать при раздельно компиляции. Представьте, что мы МОГЛИ БЫ скомпилировать ОТДЕЛЬНО такой класс, упаковать в jar и распространять

// НЕ КОМПИЛИРУЕТСЯ! ИССЛЕДУЕМ ГИПОТЕТИЧЕСКУЮ СИТУАЦИЮ! public class App < public static void f0(Throwable t) throws Exception < throw t; >public static void f1(Object ref) < char c = ref.charAt(0); >> 

А кто-то берет этот класс, добавляет в classpath и вызывает App.f0(new Throwable()) или App.f1(new Integer(42)). В таком случае JVM столкнулась бы с ситуацией, когда от нее требует бросить проверяемое исключение, которое не отследил компилятор (в случае с f0) или вызвать метод, которого нет (в случае с f1)!

Java — язык со статической типизацией (т.е. отслеживание корректности использования типов (наличие используемых полей, наличие вызываемых методов, проверка на checked исключения, . ) проводится компилятором), запрещает такое поведение. В некоторых языках (языки с динамической типизацией — отслеживание корректности использования типов проводится средой исполнения (оно разрешено, например в JavaScript).

Компилятор не пропустит этот код, хотя метод main ГАРАНТИРОВАННО НЕ ВЫБРОСИТ ИСКЛЮЧЕНИЯ

public class App < // пугаем Exception public static void main(String[] args) throws Exception < try < Throwable t = new Exception(); // и лететь будет Exception throw t; // но тут ошибка компиляции >catch (Exception e) < System.out.println("Перехвачено!"); >> > >> COMPILATION ERROR: unhandled exception: java.lang.Throwable 
public class App < // ТЕПЕРЬ пугаем Throwable public static void main(String[] args) throws Throwable < try < Throwable t = new Exception(); // а лететь будет Exception throw t; >catch (Exception e) < // и мы перехватим Exception System.out.println("Перехвачено!"); >> > >> Перехвачено! 

7. Overriding и throws

При переопределении (overriding) список исключений потомка не обязан совпадать с таковым у предка.
Но он должен быть «не сильнее» списка предка:

import java.io.FileNotFoundException; import java.io.IOException; public class Parent < // предок пугает IOException и InterruptedException public void f() throws IOException, InterruptedException <>> class Child extends Parent < // а потомок пугает только потомком IOException @Override public void f() throws FileNotFoundException <>> 

Однако тут мы попытались «расширить тип» бросаемых исключений

import java.io.IOException; public class Parent < public void f() throws IOException, InterruptedException <>> class ChildB extends Parent < @Override public void f() throws Exception <>> >> COMPILATION ERROR: overridden method does not throw 'java.lang.Exception' 

Почему можно сужать тип, но не расширять?
Рассмотрим следующую ситуацию:

// тут ошибка компиляции в Java, но, ДОПУСТИМ, ее нет public class Child extends Parent < // потомок расширил Exception до Throwable public void f() throws Throwable <>> 
public class Demo < public static void test(Parent ref) < // тут все компилируется, Parent.f() пугает Exception и мы его ловим catch try < ref.f(); >catch(Exception e) <> > > 

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

Читайте также:  Web application html framework

8. Передача свойства по наследству

 Object | Throwable(CHECKED) / \ Error(UNCHECKED) Exception(CHECKED) | RuntimeException(UNCHECKED) 

Логика расположения свойства НЕ СВЯЗАНА С НАСЛЕДОВАНИЕМ. Эту логику мы рассмотрим позже (в следующих статьях).
Однако свойство checked/unchecked пользовательских классов исключений строится ИСКЛЮЧИТЕЛЬНО НА ОСНОВЕ НАСЛЕДОВАНИЯ.
Правило крайне простое:
1. Если исключение из списка Throwable, Error, Exception, RuntimeException — то твое свойство надо просто запомнить.
2. Если ты не из списка, то твое свойство равно свойству предка. Нарушить наследование тут нельзя.

Если мы породим потомков A, B, C, D, E, F, G, H, I, J, K, L которые следующим образом наследуются от «корневища» (Throwable, Error, Exception, RuntimeException), то значение их свойства checked/unchecked можно увидеть на схеме

 Object | Throwable(CHECKED) / | \ Error(UNCHECKED) | Exception(CHECKED) | | | | | A(UNC) D(UNC) | F(C) RuntimeException(UNCHECKED) / \ | / \ | | B(UNC) C(UNC) | G(C) H(C) I(UNC) J(UNC) E(C) / \ K(UNC) L(UNC) 

Контакты

  1. показываю различные варианты применения
  2. строю усложняющуюся последовательность примеров по каждому варианту
  3. объясняю логику двигавшую авторами (по мере возможности)
  4. даю большое количество тестов (50-100) всесторонне проверяющее понимание и демонстрирующих различные комбинации
  5. даю лабораторные для самостоятельной работы

skype: GolovachCourses
email: GolovachCourses@gmail.com

Источник

Оцените статью