Разлика между Хвърляне и Хвърляния в Java

1. Въведение

В този урок ще разгледаме хвърлянията и хвърлянията в Java. Ще обясним кога трябва да използваме всеки от тях.

След това ще покажем няколко примера за основното им използване.

2. Хвърли и Хвърля

Нека започнем с бързо въведение. Тези ключови думи са свързани с обработка на изключения. Изключенията се създават, когато нормалното протичане на нашето приложение е нарушено.

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

Използваме ключова дума throw, за да изхвърлим изрично изключение от кода. Това може да е всеки метод или статичен блок. Това изключение трябва да бъде подклас на Throwable. Също така, това може да бъде самата Хвърлима . Не можем да хвърлим множество изключения с едно хвърляне .

Ключовата дума Throws може да бъде поставена в декларацията на метода. Той обозначава кои изключения могат да бъдат изхвърлени от този метод. Трябва да се справим с тези изключения с try-catch.

Тези две ключови думи не са взаимозаменяеми!

3. Хвърли в Java

Нека да разгледаме един основен пример с хвърляне на изключение от метода.

На първо място, представете си, че пишем прост калкулатор. Една от основните аритметични операции е разделянето. Поради това бяхме помолени да приложим тази функция:

public double divide(double a, double b) { return a / b; }

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

Да го направим:

public double divide(double a, double b) { if (b == 0) { throw new ArithmeticException("Divider cannot be equal to zero!"); } return a / b; }

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

3.1. Добри практики

Винаги трябва да предпочитаме най-конкретното изключение. Трябва да намерим клас, който е най-подходящ за нашето изключително събитие. Например, хвърлете NumberFormatException вместо IllegalArgumentException. Трябва да избягваме да създаваме неспецифично изключение .

Например има пакет Integer в пакета java.lang . Нека да разгледаме тази на декларацията на фабричния метод:

public static Integer valueOf(String s) throws NumberFormatException 

Това е статичен фабричен метод, който създава Integer екземпляр от String. В случай на грешен входен низ , методът ще изхвърли NumberFormatException.

Добра идея е да дефинираме нашето собствено, по-описателно изключение. В нашия клас Калкулатор, който може да бъде например DivideByZeroException.

Нека да разгледаме примерното изпълнение:

public class DivideByZeroException extends RuntimeException { public DivideByZeroException(String message) { super(message); } }

3.2. Опаковане на съществуващо изключение

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

Нека започнем с дефинирането на нашето собствено изключение:

public class DataAcessException extends RuntimeException { public DataAcessException(String message, Throwable cause) { super(message, cause); } }

Конструкторът взема два параметъра: съобщение за изключение и причина, която може да бъде всеки подклас на Throwable.

Нека напишем фалшива реализация за функцията findAll () :

public List findAll() throws SQLException { throw new SQLException(); }

Сега в SimpleService нека извикаме функция на хранилището, което може да доведе до SQLException:

public void wrappingException() { try { personRepository.findAll(); } catch (SQLException e) { throw new DataAccessException("SQL Exception", e); } }

Ние сме повторно хвърляне SQLException увити в нашата собствена изключение нарича DataAccessException. Всичко се проверява от следния тест:

@Test void whenSQLExceptionIsThrown_thenShouldBeRethrownWithWrappedException() { assertThrows(DataAccessException.class, () -> simpleService.wrappingException()); }

Има две причини да направите това. На първо място, използваме опаковане на изключения, тъй като останалата част от кода не трябва да знае за всяко възможно изключение в системата.

Също така компонентите от по-високо ниво не трябва да знаят нито за компонентите от долното ниво, нито за изключенията, които те хвърлят.

3.3. Multi-Catch с Java

Понякога използваните от нас методи могат да хвърлят много различни изключения.

Нека да разгледаме по-обширен блок за изпробване:

try { tryCatch.execute(); } catch (ConnectionException | SocketException ex) { System.out.println("IOException"); } catch (Exception ex) { System.out.println("General exception"); }

The execute method can throw three exceptions: SocketException, ConnectionException, Exception. The first catch block will catch ConnectionException or SocketException. The second catch block would catch Exception or any other subclass of Exception. Remember, that we should always catch a more detailed exception first.

We can swap the order of our catch blocks. Then, we'd never catch SocketException and ConnectionException because everything will go to the catch with Exception.

4. Throws in Java

We add throws to the method declaration.

Let's take a look at one of our previous method declaration:

public static void execute() throws SocketException, ConnectionException, Exception

The method may throw multiple exceptions. They are comma-separated at the end of a method declaration. We can put both, checked and unchecked exceptions in the throws. We have described the difference between them below.

4.1. Checked and Unchecked Exceptions

A checked exception means that it's checked at the compile time. Note, that we must handle this exception. Otherwise, a method must specify an exception by using throws keyword.

The most common checked exceptions are IOException, FileNotFoundException, ParseException. FileNotFoundException may be thrown when we create FileInputStream from File.

There's a short example:

File file = new File("not_existing_file.txt"); try { FileInputStream stream = new FileInputStream(file); } catch (FileNotFoundException e) { e.printStackTrace(); }

We can avoid using try-catch block by adding throws to the method declaration:

private static void uncheckedException() throws FileNotFoundException { File file = new File("not_existing_file.txt"); FileInputStream stream = new FileInputStream(file); }

Unfortunately, a higher level function still has to handle this exception. Otherwise, we have to put this exception in method declaration with throws keyword.

As the opposite, unchecked exceptions aren't checked at the compile time.

The most common unchecked exceptions are: ArrayIndexOutOfBoundsException, IllegalArgumentException, NullPointerException.

Unchecked exceptions are thrown during runtime. The following code will throw a NullPointerException. Probably it's one of the most common exceptions in Java.

Calling a method on a null reference will result in this exception:

public void runtimeNullPointerException() { String a = null; a.length(); }

Let's verify this behavior in the test:

@Test void whenCalled_thenNullPointerExceptionIsThrown() { assertThrows(NullPointerException.class, () -> simpleService.runtimeNullPointerException()); }

Моля, не забравяйте, че този код и тест нямат много смисъл. Само с учебна цел се обясняват изключенията по време на работа.

В Java всеки подклас на Error и RuntimeException е непроверено изключение. Проверено изключение е всичко останало от клас Throwable .

5. Заключение

В тази статия обсъдихме разликата между две ключови думи в Java: хвърляне и хвърляне. Преминахме през основната употреба и говорихме малко за добри практики . Тогава говорихме за проверени и непроверени изключения.

Както винаги, изходният код може да бъде намерен в нашия GitHub.

Ако искате да влезете по-задълбочено в обработката на изключения в Java, моля, разгледайте нашата статия за Java изключенията.