спробувати / ловити проти кидків Виняток


117

Чи ці еквівалентні заяви кодові еквівалентні? Чи є різниця між ними?

private void calculateArea() throws Exception {
    ....do something
}

private void calculateArea() {
    try {
        ....do something
    } catch (Exception e) {
        showException(e);
    }
}

3
Насправді не відповідь, але вас може зацікавити стаття Неделя Батчелдера « Винятки в тропічному лісі» , яка допомагає пояснити загальні випадки, коли слід віддати перевагу одному чи іншому стилю.
Даніель Приден

1
замість того, щоб "showException (e)" "у запиті, ви запитували, чи не було ви" кидає e "в улов замість цього (чи не маєте спробувати / catch взагалі)?
MacGyver

Відповіді:


146

Так, є величезна різниця - останній поглинає виняток (показує це, правда,), тоді як перший дозволить йому поширюватися. (Я припускаю, що showExceptionце не перекидає.)

Тож якщо зателефонувати за першим методом і "зробити щось" не вдасться, то абоненту доведеться обробляти виняток. Якщо ви викличте другий метод і «зробити що - то» не вдається, то абонент не бачитиме виключення взагалі ... що це взагалі погано, якщо showExceptionНЕ дійсно обробив виняток, закріплене , що було неправильно, і взагалі впевнилися що calculateAreaдосяг своєї мети.

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


12
Коли ви згадуєте про те, що "Якщо тільки справді не вирішено виняток", це чудовий момент. Я просто подумав, що додам, що ловлі "винятку" самі по собі рідко призводять до розумного "поводження" з фактичним винятком, тому люди рекомендують вам ловити найбільш конкретний виняток.
Білл К

17
+1. Тому що Джон Скіт потребує більшої репутації. О, і відповідь була також хорошою.
Джонатан Спіллер

20

По-перше throws Exception, тому абонент повинен обробляти Exception. По-друге, він ловить і обробляє Exceptionвсередині, тому абонент не повинен виконувати будь-яку обробку винятків.


Тож у двох словах, я завжди повинен використовувати другий. Маю рацію? Перший - це фактично метод, який використовується в різних точках програми. Ось чому я вирішив зібрати разом інструкції для подальшого використання, але зробивши це, я тепер розумію, що Т зробив велику помилку ..
carlos

9
Ні, обидва зразки потрібні. Якщо ваш метод може обробити виняток, використовуйте другий шаблон, якщо ні, використовуйте перший, щоб повідомити абонента.
Андреас Долк

Яку версію ви використовуєте, залежить від ваших вимог - в основному, на якому рівні вам потрібно обробляти цей виняток. Абонента потрібно кодувати відповідно. Якщо абонент викликав першу версію, а ви замінюєте визначення методу другою версією, ваш код абонента буде змушений обробляти виняток, оскільки це перевірений виняток.
samitgaur

16

Так. Версія, яка декларує throws Exception, вимагатиме виклику коду для обробки винятку, тоді як версія, яка явно обробляє його, не буде.

тобто просто:

performCalculation();

порівняно з перенесенням тягаря обміну винятком на абонента:

try {
    performCalculation();
catch (Exception e) {
    // handle exception
}

6

Так, між ними існує велика різниця. У першому блоці коду ви передаєте виняток коду виклику. У другому блоці коду ви самі це обробляєте. Який метод правильний, повністю залежить від того, що ви робите. У деяких випадках ви хочете, щоб ваш код обробляв виняток (якщо файл не знайдено, і ви хочете створити його, наприклад), а в інших - викликовий код обробляє виняток (файл не знайдено і їм потрібно вказати нове або створити його).

Взагалі кажучи, ви також не хочете співати загальний виняток. Натомість вам захочеться спіймати лише конкретні, наприклад, FileNotFoundExceptionабо IOExceptionтому, що вони можуть означати різні речі.


3

Є один конкретний сценарій, коли ми не можемо використовувати кидки, ми повинні використати try-catch. Існує правило "Переотриманий метод не може викидати будь-який додатковий виняток, крім того, що кидає його батьківський клас". Якщо є якісь додаткові винятки, з якими слід обробляти за допомогою try-catch. Розглянемо цей фрагмент коду. Існує простий базовий клас

package trycatchvsthrows;

public class Base {
    public void show()
    {
        System.out.println("hello from base");
    }
}

і це похідний клас:

package trycatchvsthrows;

public class Derived extends Base {

    @Override
    public void show()   {
        // TODO Auto-generated method stub
        super.show();

        Thread thread= new Thread();
        thread.start();
        try {
            thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // thread.sleep(10);
        // here we can not use public void show() throws InterruptedException 
        // not allowed
    }
}

Коли нам потрібно викликати thread.sleep (), ми змушені використовувати try-catch, тут ми не можемо використовувати:

 public void show() throws InterruptedException

тому що перекритий метод не може кинути зайві винятки.


Я вважаю, що не всі знають про цей застереження. Добре загострений.
ivanleoncz

1

Я припускаю, що під "ідентичним" ви маєте на увазі поведінку.

Поведінку функції можна визначити:

1) Повернуте значення

2) Викинуті винятки

3) Побічні ефекти (тобто зміни в купі, файловій системі тощо)

У цьому випадку перший метод поширює будь-який виняток, тоді як другий викидає не перевірені винятки, а також проковтує більшість неперевірених винятків, тому поведінка є іншою.

Однак якщо ви гарантуєте, що "зроби щось" ніколи не викидає виняток, то поведінка буде ідентичною (хоча компілятор вимагатиме від абонента обробляти виняток, у першій версії)

--edit--

З погляду дизайну API, методи абсолютно різні за своїм контрактом. Також не рекомендується викидати клас Виняток. Спробуйте кинути щось більш конкретне, щоб дозволити абоненту краще обробляти виняток.


1

Якщо ви кинули виняток, дочірній метод (який це перевищує) повинен обробляти виняток

приклад:

class A{
public void myMethod() throws Exception{
 //do something
}
}

A a=new A();
try{
a.myMethod();
}catch Exception(e){
//handle the exception
}

0

Багато разів ви хочете, щоб абонент обробляв виняток. Скажімо, у вас є метод виклику абонента, який викликає інший метод, який викликає інший метод, замість того, щоб кожен метод обробляв виняток, ви можете просто обробити його у абонента. Якщо ви не хочете зробити щось за допомогою одного з методів, коли цей метод не вдається.


0

Користувачеві цього методу потрібно буде або зафіксувати це виняток, або оголосити його повторним в підписанні методу.

private void calculateArea() throws Exception {
    // Do something
}

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

private void calculateArea() {
    try {
        // Do something

    } catch (Exception e) {
        showException(e);
    }
}

0
private void calculateArea() throws Exception {
    ....do something
}

Це викидає виняток, тому абонент несе відповідальність за обробку цього винятку, але якщо виклик не обробляє виняток, можливо, він буде наданий jvm, що може призвести до ненормального припинення програми.

Тоді як у другому випадку:

private void calculateArea() {
    try {
        ....do something
    } catch (Exception e) {
        showException(e);
    }
}

Тут виняток обробляє виклик, тому немає шансів аномального припинення програми.

Спробуйте-ловитиРекомендований підхід .

ІМО,

  • Ключове слово Throw в основному використовується з перевіреними винятками для переконання компілятора, але це не гарантує нормального припинення програми.

  • Ключове слово Throw делегує відповідальність за обробку виключень
    абоненту (JVM або інший метод).

  • Ключове слово Throw потрібно лише для перевірених винятків, для неперевірених винятків не використовується ключове слово.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.