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


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.