Ricerca…


introduzione

Oggetti di tipo Throwable e i suoi sottotipi possono essere inviati allo stack con la parola chiave throw e catturati con try…catch statements.

Sintassi

  • void someMethod () genera la dichiarazione del metodo SomeException {} //, forza l'intercettazione dei chiamanti del metodo se SomeException è un tipo di eccezione verificata

  • provare {

    someMethod(); //code that might throw an exception 
    

    }

  • catch (SomeException e) {

     System.out.println("SomeException was thrown!"); //code that will run if certain exception (SomeException) is thrown
    

    }

  • finalmente {

     //code that will always run, whether try block finishes or not
    

    }

Cattura un'eccezione con try-catch

Un'eccezione può essere catturata e gestita usando la try...catch . (In effetti le dichiarazioni di try prendono altre forme, come descritto in altri esempi su try...catch...finally e try-with-resources .)

Prova a prendere con un blocco di cattura

La forma più semplice ha questo aspetto:

try {
    doSomething();
} catch (SomeException e) {
    handle(e);
}
// next statement

Il comportamento di un semplice try...catch è il seguente:

  • Le istruzioni nel blocco try vengono eseguite.
  • Se nessuna eccezione è generata dalle dichiarazioni nel blocco try , il controllo passa alla successiva dichiarazione dopo il try...catch .
  • Se viene lanciata un'eccezione all'interno del blocco try .
    • L'oggetto eccezione viene verificato per verificare se si tratta di un'istanza di SomeException o di un sottotipo.
    • Se lo è, il blocco catch attira l'eccezione:
      • La variabile e è associata all'oggetto eccezione.
      • Il codice all'interno del blocco catch viene eseguito.
      • Se quel codice genera un'eccezione, l'eccezione appena generata viene propagata al posto di quella originale.
      • Altrimenti, il controllo passa alla dichiarazione successiva dopo il try...catch .
    • Se non lo è, l'eccezione originale continua a propagarsi.

Prova a catturare con più catture

Un try...catch può anche avere più blocchi catch . Per esempio:

try {
    doSomething();
} catch (SomeException e) {
    handleOneWay(e)
} catch (SomeOtherException e) {
    handleAnotherWay(e);
}
// next statement

Se ci sono più blocchi catch , vengono provati uno alla volta iniziando dal primo, finché non viene trovata una corrispondenza per l'eccezione. Il gestore corrispondente viene eseguito (come sopra) e quindi il controllo viene passato all'istruzione successiva dopo l'istruzione try...catch . I blocchi catch dopo quello che corrisponde vengono sempre saltati, anche se il codice del gestore genera un'eccezione .

La strategia di abbinamento "top down" ha conseguenze per i casi in cui le eccezioni nei blocchi catch non sono disgiunte. Per esempio:

try {
    throw new RuntimeException("test");
} catch (Exception e) {
    System.out.println("Exception");
} catch (RuntimeException e) {
    System.out.println("RuntimeException");
}

Questo snippet di codice genererà "Eccezione" anziché "RuntimeException". Poiché RuntimeException è un sottotipo di Exception , il primo (più generale) catch verrà confrontato. La seconda catch (più specifica) non verrà mai eseguita.

La lezione da imparare da questo è che i blocchi di catch più specifici (in termini di tipi di eccezione) dovrebbero apparire per primi, e quelli più generali dovrebbero essere gli ultimi. (Alcuni compilatori Java ti avviseranno se una catch non può mai essere eseguita, ma questo non è un errore di compilazione.)

Blocchi di cattura multi-eccezione

Java SE 7

A partire da Java SE 7, un singolo blocco catch può gestire un elenco di eccezioni non correlate. Il tipo di eccezione è elencato, separato da un simbolo di barra verticale ( | ). Per esempio:

try {
    doSomething();
} catch (SomeException | SomeOtherException e) {
    handleSomeException(e);
} 

Il comportamento di un'eccezione a più eccezioni è un'estensione semplice per il caso a eccezione singola. Il catch corrisponde se l'eccezione generata soddisfa (almeno) una delle eccezioni elencate.

C'è qualche sottigliezza aggiuntiva nelle specifiche. Il tipo di e è un'unione sintetica dei tipi di eccezione nell'elenco. Quando viene utilizzato il valore di e , il suo tipo statico è il supertipo meno comune dell'unione di tipo. Tuttavia, se e viene ripubblicato all'interno del blocco catch , i tipi di eccezioni lanciati sono i tipi nell'unione. Per esempio:

public void method() throws IOException, SQLException
    try {
        doSomething();
    } catch (IOException | SQLException e) {
        report(e);
        throw e;
    }

In quanto sopra, IOException e SQLException sono eccezioni controllate il cui supertipo minimo comune è Exception . Ciò significa che il metodo del report deve corrispondere al report(Exception) . Tuttavia, il compilatore sa che il throw può generare solo IOException o SQLException . Pertanto, il method può essere dichiarato come throws IOException, SQLException piuttosto che throws Exception . (Che è una buona cosa: vedi Pitfall - Throwable Throwable, Exception, Error o RuntimeException .)

Lanciare un'eccezione

Il seguente esempio mostra le basi del lancio di un'eccezione:

public void checkNumber(int number) throws IllegalArgumentException {
    if (number < 0) {
        throw new IllegalArgumentException("Number must be positive: " + number);
    }
}

L'eccezione è lanciata sulla terza linea. Questa affermazione può essere suddivisa in due parti:

  • new IllegalArgumentException(...) sta creando un'istanza della classe IllegalArgumentException , con un messaggio che descrive l'errore segnalato da tale eccezione.

  • throw ... lancia quindi l'oggetto eccezione.

Quando viene generata l'eccezione, le istruzioni di chiusura vengono terminate in modo anomalo finché non viene gestita l'eccezione. Questo è descritto in altri esempi.

È buona norma creare e lanciare l'oggetto eccezione in una singola istruzione, come mostrato sopra. È inoltre buona norma includere un messaggio di errore significativo nell'eccezione per aiutare il programmatore a comprendere la causa del problema. Tuttavia, questo non è necessariamente il messaggio che dovresti mostrare all'utente finale. (Per cominciare, Java non ha supporto diretto per l'internazionalizzazione dei messaggi di eccezione.)

Ci sono un paio di altri punti da fare:

  • Abbiamo dichiarato il checkNumber come throws IllegalArgumentException . Questo non era strettamente necessario, poiché IllegalArgumentException è un'eccezione controllata; vedere La gerarchia delle eccezioni Java - Eccezioni non selezionate e controllate . Tuttavia, è buona pratica farlo e includere anche le eccezioni generate dai commenti javadoc di un metodo.

  • Codice immediatamente dopo che una dichiarazione di throw è irraggiungibile . Quindi se abbiamo scritto questo:

     throw new IllegalArgumentException("it is bad");
     return;
    

    il compilatore avrebbe segnalato un errore di compilazione per la dichiarazione di return .

Eccezione concatenata

Molte eccezioni standard hanno un costruttore con un secondo argomento di cause oltre all'argomento di message convenzionale. La cause consente di concatenare eccezioni. Ecco un esempio.

Per prima cosa definiamo un'eccezione non controllata che la nostra applicazione sta per gettare quando incontra un errore non recuperabile. Si noti che abbiamo incluso un costruttore che accetta un argomento di cause .

    public class AppErrorException extends RuntimeException {
        public AppErrorException() {
            super();
        }

        public AppErrorException(String message) {
            super(message);
        }

        public AppErrorException(String message, Throwable cause) {
            super(message, cause);
        }
    }

Successivamente, ecco un codice che illustra il concatenamento delle eccezioni.

    public String readFirstLine(String file) throws AppErrorException {
        try (Reader r = new BufferedReader(new FileReader(file))) {
            String line = r.readLine();
            if (line != null) {
                return line;
            } else {
                throw new AppErrorException("File is empty: " + file);
            }
        } catch (IOException ex) {
            throw new AppErrorException("Cannot read file: " + file, ex);
        }
    }

Il throw all'interno del blocco try rileva un problema e lo segnala tramite un'eccezione con un semplice messaggio. Al contrario, il throw all'interno del blocco catch sta gestendo IOException avvolgendolo in una nuova (controllata) eccezione. Tuttavia, non sta gettando via l'eccezione originale. Passando la IOException come cause , la registriamo in modo che possa essere stampata nello stacktrace, come spiegato in Creazione e lettura di stacktraces .

Eccezioni personalizzate

Nella maggior parte dei casi, è più semplice da un punto di vista della progettazione del codice utilizzare le classi di Exception generiche esistenti quando si generano eccezioni. Questo è particolarmente vero se hai solo bisogno dell'eccezione per portare un semplice messaggio di errore. In tal caso, RuntimeException di solito è preferito, poiché non è un'eccezione controllata. Esistono altre classi di eccezioni per classi comuni di errori:

I casi in cui si vuole utilizzare una classe eccezione personalizzata sono i seguenti:

  • Stai scrivendo un'API o una libreria per l'utilizzo da parte di altri e desideri consentire agli utenti della tua API di essere in grado di catturare e gestire in modo specifico le eccezioni dalla tua API e di distinguere tali eccezioni da altre eccezioni più generiche .
  • Stai lanciando eccezioni per un tipo specifico di errore in una parte del tuo programma, che vuoi catturare e gestire in un'altra parte del tuo programma, e vuoi essere in grado di distinguere questi errori da altri errori più generici.

È possibile creare eccezioni personalizzate estendendo RuntimeException per un'eccezione non controllata o verificata l'eccezione estendendo qualsiasi Exception che non sia anche sottoclasse di RuntimeException , in quanto:

Le sottoclassi di Eccezione che non sono anche sottoclassi di RuntimeException sono eccezioni controllate

public class StringTooLongException extends RuntimeException {
    // Exceptions can have methods and fields like other classes
    // those can be useful to communicate information to pieces of code catching
    // such an exception
    public final String value;
    public final int maximumLength;

    public StringTooLongException(String value, int maximumLength){
        super(String.format("String exceeds maximum Length of %s: %s", maximumLength, value));
        this.value = value;
        this.maximumLength = maximumLength;
    }
}

Quelli possono essere usati solo come eccezioni predefinite:

void validateString(String value){
    if (value.length() > 30){
        throw new StringTooLongException(value, 30);
    }
}

E i campi possono essere utilizzati dove viene rilevata e gestita l'eccezione:

void anotherMethod(String value){
    try {
        validateString(value);
    } catch(StringTooLongException e){
        System.out.println("The string '" + e.value + 
                "' was longer than the max of " + e.maximumLength );
    }
}

Tieni presente che, secondo la documentazione Java di Oracle :

[...] Se ci si può ragionevolmente aspettare che un client recuperi da un'eccezione, rendi un'eccezione controllata. Se un client non può eseguire operazioni di ripristino dall'eccezione, renderlo un'eccezione non controllata.

Di Più:

La dichiarazione try-with-resources

Java SE 7

Come illustra l'esempio di dichiarazione try-catch-final , il cleanup delle risorse che utilizza una clausola finally richiede una quantità significativa di codice "boiler-plate" per implementare correttamente le edge case. Java 7 fornisce un modo molto più semplice per affrontare questo problema nella forma dell'istruzione try-with-resources .

Cos'è una risorsa?

Java 7 ha introdotto l'interfaccia java.lang.AutoCloseable per consentire la gestione delle classi utilizzando l'istruzione try-with-resources . Le istanze di classi che implementano AutoCloseable sono indicate come risorse . Questi in genere devono essere smaltiti in modo tempestivo piuttosto che affidarsi al garbage collector per smaltirli.

L'interfaccia AutoCloseable definisce un singolo metodo:

public void close() throws Exception

Un metodo close() dovrebbe disporre della risorsa in modo appropriato. La specifica afferma che dovrebbe essere sicuro chiamare il metodo su una risorsa che è già stata eliminata. Inoltre, le classi che implementano l' Autocloseable sono fortemente incoraggiate a dichiarare il metodo close() per generare un'eccezione più specifica di Exception , o nessuna eccezione.

Una vasta gamma di classi e interfacce Java standard implementano AutoCloseable . Questi includono:

  • InputStream , OutputStream e le loro sottoclassi
  • Reader , Writer e le loro sottoclassi
  • Socket e ServerSocket e relative sottoclassi
  • Channel e le sue sottoclassi, e
  • il JDBC interfaccia Connection , Statement e ResultSet e le loro sottoclassi.

Anche le classi di applicazioni e di terze parti possono farlo.

La dichiarazione base di prova con la risorsa

La sintassi di un try-with-resources si basa su forme classiche try-catch , try-finally e try-catch-finally . Ecco un esempio di una forma "base"; cioè la forma senza un catch o, finally .

try (PrintStream stream = new PrintStream("hello.txt")) {
    stream.println("Hello world!");
}

Le risorse da gestire sono dichiarate come variabili nella (...) sezione dopo la clausola try . Nell'esempio sopra, dichiariamo un stream variabili di risorsa e lo inizializziamo su PrintStream appena creato.

Una volta che le variabili della risorsa sono state inizializzate, viene eseguito il blocco try . Al termine, stream.close() verrà chiamato automaticamente per garantire che la risorsa non perda. Si noti che la chiamata close() avviene indipendentemente dal completamento del blocco.

Le dichiarazioni avanzate di try-with-resource

L'istruzione try-with-resources può essere migliorata con catch blocchi catch e finally , come con la sintassi pre-Java 7 try-catch-finally . Il seguente frammento di codice aggiunge un blocco catch al precedente per gestire l' PrintStream FileNotFoundException che può essere PrintStream costruttore PrintStream :

try (PrintStream stream = new PrintStream("hello.txt")) {
    stream.println("Hello world!");
} catch (FileNotFoundException ex) {
    System.err.println("Cannot open the file");
} finally {
    System.err.println("All done");
}

Se l'inizializzazione della risorsa o il blocco try genera l'eccezione, verrà eseguito il blocco catch . Il blocco finally verrà sempre eseguito, come in una dichiarazione try-catch-finally convenzionale.

Ci sono un paio di cose da notare però:

  • La variabile di risorsa è fuori ambito nel catch e finally blocchi.
  • La pulizia delle risorse avverrà prima che l'istruzione tenti di far corrispondere il blocco catch .
  • Se la pulizia automatica delle risorse ha generato un'eccezione, potrebbe essere catturata in uno dei blocchi catch .

Gestire più risorse

I frammenti di codice sopra mostrano una singola risorsa che viene gestita. In effetti, try-with-resources può gestire più risorse in un'unica istruzione. Per esempio:

try (InputStream is = new FileInputStream(file1);
     OutputStream os = new FileOutputStream(file2)) {
    // Copy 'is' to 'os'
}

Questo si comporta come ti aspetteresti. Sia is che os vengono chiusi automaticamente alla fine del blocco try . Ci sono un paio di punti da notare:

  • Le inizializzazioni si verificano nell'ordine di codice e gli inizializzatori di variabili di risorse successive possono utilizzare i valori di quelli precedenti.
  • Tutte le variabili di risorsa inizializzate correttamente verranno eliminate.
  • Le variabili delle risorse vengono pulite in ordine inverso rispetto alle loro dichiarazioni.

Pertanto, nell'esempio di cui sopra, is è inizializzato prima os e ripulito dopo di essa, e is verrà pulito se c'è un'eccezione durante l'inizializzazione os .

Equivalenza di try-with-resource e try-catch-finally classico

La specifica del linguaggio Java specifica il comportamento delle forme try-with-resource in termini della classica dichiarazione try-catch-finally . (Si prega di fare riferimento al JLS per tutti i dettagli.)

Ad esempio, questa base di prova con risorsa :

try (PrintStream stream = new PrintStream("hello.txt")) {
    stream.println("Hello world!");
}

è definito come equivalente a questo try-catch-finally :

// Note that the constructor is not part of the try-catch statement
PrintStream stream = new PrintStream("hello.txt");

// This variable is used to keep track of the primary exception thrown
// in the try statement. If an exception is thrown in the try block,
// any exception thrown by AutoCloseable.close() will be suppressed.
Throwable primaryException = null;

// The actual try block
try {
    stream.println("Hello world!");
} catch (Throwable t) {
    // If an exception is thrown, remember it for the finally block
    primaryException = t;
    throw t;
} finally {
    if (primaryException == null) {
        // If no exception was thrown so far, exceptions thrown in close() will
        // not be caught and therefore be passed on to the enclosing code.
        stream.close();
    } else {
        // If an exception has already been thrown, any exception thrown in
        // close() will be suppressed as it is likely to be related to the
        // previous exception. The suppressed exception can be retrieved
        // using primaryException.getSuppressed().
        try {
            stream.close();
        } catch (Throwable suppressedException) {
            primaryException.addSuppressed(suppressedException);
        }
    }
}

(Il JLS specifica che le variabili t e primaryException effettive saranno invisibili al normale codice Java.)

La forma migliorata di try-with-resources è specificata come un'equivalenza con il modulo base. Per esempio:

try (PrintStream stream = new PrintStream(fileName)) {
    stream.println("Hello world!");
} catch (NullPointerException ex) {
    System.err.println("Null filename");
} finally {
    System.err.println("All done");    
}

è equivalente a:

try {
    try (PrintStream stream = new PrintStream(fileName)) {
        stream.println("Hello world!");
    }
} catch (NullPointerException ex) {
    System.err.println("Null filename");
} finally {
    System.err.println("All done");    
}    

Creazione e lettura di stacktraces

Quando viene creato un oggetto eccezione (cioè quando lo si è new ), il costruttore Throwable acquisisce informazioni sul contesto in cui è stata creata l'eccezione. In seguito, queste informazioni possono essere visualizzate sotto forma di stacktrace, che può essere utilizzato per aiutare a diagnosticare il problema che ha causato l'eccezione in primo luogo.

Stampa di uno stacktrace

Stampare uno stacktrace è semplicemente una questione di chiamare il metodo printStackTrace() . Per esempio:

try {
    int a = 0;
    int b = 0;
    int c = a / b;
} catch (ArithmeticException ex) {
    // This prints the stacktrace to standard output
    ex.printStackTrace();
}

Il metodo printStackTrace() senza argomenti verrà stampato sull'output standard dell'applicazione; cioè l'attuale System.out . Esistono anche printStackTrace(PrintStream) e printStackTrace(PrintWriter) che vengono stampati su uno Stream o su un Writer specificato.

Gli appunti:

  1. Lo stacktrace non include i dettagli dell'eccezione stessa. Puoi usare il metodo toString() per ottenere quei dettagli; per esempio

       // Print exception and stacktrace
       System.out.println(ex);
       ex.printStackTrace();
    
  2. La stampa Stacktrace dovrebbe essere usata con parsimonia; see Pitfall - Stacktraces eccessivi o inappropriati . È spesso preferibile utilizzare un framework di registrazione e passare l'oggetto di eccezione da registrare.

Capire uno stacktrace

Considera il seguente semplice programma composto da due classi in due file. (Abbiamo mostrato i nomi dei file e i numeri di riga aggiunti a scopo illustrativo.)

File: "Main.java"
1   public class Main {
2       public static void main(String[] args) {
3           new Test().foo();
4       }
5   }

File: "Test.java"
1   class Test {
2       public void foo() {
3           bar();
4       }
5   
6       public int bar() {
7           int a = 1;
8           int b = 0;
9           return a / b;
10      }

Quando questi file sono compilati ed eseguiti, otterremo il seguente output.

Exception in thread "main" java.lang.ArithmeticException: / by zero
        at Test.bar(Test.java:9)
        at Test.foo(Test.java:3)
        at Main.main(Main.java:3)

Leggiamo questa riga alla volta per capire cosa ci sta dicendo.

La riga n. 1 ci dice che il thread chiamato "main" è terminato a causa di un'eccezione non rilevata. Il nome completo dell'eccezione è java.lang.ArithmeticException e il messaggio di eccezione è "/ per zero".

Se cerchiamo i javadoc per questa eccezione, si dice:

Gettato quando si è verificata una condizione aritmetica eccezionale. Ad esempio, un intero "divide per zero" genera un'istanza di questa classe.

In effetti, il messaggio "/ per zero" indica chiaramente che la causa dell'eccezione è che alcuni codici hanno tentato di dividere qualcosa per zero. Ma cosa?

Le restanti 3 linee sono la traccia dello stack. Ogni linea rappresenta una chiamata al metodo (o al costruttore) nello stack delle chiamate e ognuna ci dice tre cose:

  • il nome della classe e del metodo che è stato eseguito,
  • il nome file del codice sorgente,
  • il numero di riga del codice sorgente dell'istruzione che si stava eseguendo

Queste linee di uno stacktrace sono elencate con il frame per la chiamata corrente in alto. Il frame superiore nell'esempio sopra riportato è nel metodo Test.bar e alla riga 9 del file Test.java. Questa è la seguente riga:

    return a / b;

Se guardiamo prima un paio di righe nel file dove b è inizializzato, è chiaro che b avrà il valore zero. Possiamo dire senza alcun dubbio che questa è la causa dell'eccezione.

Se dovessimo andare oltre, possiamo vedere dallo stacktrace che bar() stato chiamato da foo() alla riga 3 di Test.java, e che foo() stato a sua volta chiamato da Main.main() .

Nota: i nomi di classi e metodi nei frame di stack sono i nomi interni per le classi e i metodi. Dovrai riconoscere i seguenti casi insoliti:

  • Una classe nidificata o interiore avrà l'aspetto di "OuterClass $ InnerClass".
  • Una classe interna anonima avrà l'aspetto di "OuterClass $ 1", "OuterClass $ 2", eccetera.
  • Quando viene eseguito il codice in un costruttore, inizializzatore campo istanza o un blocco inizializzatore istanza, il nome del metodo sarà "".
  • Quando viene eseguito il codice di un inizializzatore di campo statico o di un blocco di inizializzazione statico, il nome del metodo sarà "".

(In alcune versioni di Java, il codice di formattazione dello stacktrace rileverà ed eliderà sequenze ripetute dello stackframe, come può accadere quando un'applicazione fallisce a causa della ricorsione eccessiva.)

Eccezione di concatenamento e stacker nidificati

Java SE 1.4

Il concatenamento di eccezioni si verifica quando un pezzo di codice cattura un'eccezione e quindi crea e ne genera uno nuovo, passando la prima eccezione come causa. Ecco un esempio:

File: Test,java
1   public class Test {
2      int foo() {
3           return 0 / 0;
4      }
5
6       public Test() {
7           try {
8               foo();
9           } catch (ArithmeticException ex) {
10              throw new RuntimeException("A bad thing happened", ex);
11          }
12      }
13
14      public static void main(String[] args) {
15          new Test();
16      }
17  }

Quando la classe sopra è compilata ed eseguita, otteniamo il seguente stacktrace:

Exception in thread "main" java.lang.RuntimeException: A bad thing happened
        at Test.<init>(Test.java:10)
        at Test.main(Test.java:15)
Caused by: java.lang.ArithmeticException: / by zero
        at Test.foo(Test.java:3)
        at Test.<init>(Test.java:8)
        ... 1 more

Lo stacktrace inizia con il nome della classe, il metodo e lo stack di chiamate per l'eccezione che (in questo caso) ha causato l'arresto anomalo dell'applicazione. Questo è seguito da una riga "Causato da:" che segnala l'eccezione di cause . Vengono riportati il ​​nome della classe e il messaggio, seguiti dai frame dello stack dell'eccezione causa. La traccia termina con un "... N more" che indica che gli ultimi N frame sono gli stessi dell'eccezione precedente.

"Caused by:" è incluso solo nell'output quando la cause dell'eccezione primaria non è null ). Le eccezioni possono essere concatenate indefinitamente, e in tal caso lo stacktrace può avere più tracce "Causate da:".

Nota: il meccanismo di cause stato esposto solo nell'API Throwable in Java 1.4.0. Prima di ciò, il concatenamento delle eccezioni doveva essere implementato dall'applicazione utilizzando un campo di eccezioni personalizzato per rappresentare la causa e un metodo printStackTrace personalizzato.

Catturare uno stacktrace come una stringa

A volte, un'applicazione deve essere in grado di acquisire uno stacktrace come String Java, in modo che possa essere utilizzato per altri scopi. L'approccio generale per fare ciò è creare un OutputStream o un Writer temporaneo che scriva su un buffer in memoria e lo passi a printStackTrace(...) .

Le librerie Apache Commons e Guava forniscono metodi di utilità per acquisire uno stacktrace come stringa:

org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(Throwable)

com.google.common.base.Throwables.getStackTraceAsString(Throwable)

Se non è possibile utilizzare librerie di terze parti nella propria base di codice, utilizzare il seguente metodo per eseguire l'attività:

   /**
     * Returns the string representation of the stack trace.
     *
     * @param throwable the throwable
     * @return the string.
     */
    public static String stackTraceToString(Throwable throwable) {
        StringWriter stringWriter = new StringWriter();
        throwable.printStackTrace(new PrintWriter(stringWriter));
        return stringWriter.toString();
    }

Si noti che se si intende analizzare lo stacktrace, è più semplice utilizzare getStackTrace() e getCause() piuttosto che tentare di analizzare uno stacktrace.

Gestione di InterruptedException

InterruptedException è una bestia che confonde - si presenta in metodi apparentemente innocui come Thread.sleep() , ma Thread.sleep() modo errato porta a un codice difficile da gestire che si comporta male negli ambienti concorrenti.

Nella sua forma più semplice, se viene rilevata un'interruzione di InterruptedException , significa che qualcuno, da qualche parte, ha chiamato Thread.interrupt() sul thread in cui è attualmente in esecuzione il codice. Potresti essere incline a dire "È il mio codice! Non lo interromperò mai! " e quindi fare qualcosa del genere:

// Bad. Don't do this.
try {
  Thread.sleep(1000);
} catch (InterruptedException e) {
  // disregard
}

Ma questo è esattamente il modo sbagliato di gestire un evento "impossibile" che si verifica. Se sai che la tua applicazione non incontrerà mai un'interruzione di InterruptedException , dovresti trattare tale evento come una grave violazione delle ipotesi del tuo programma e uscire il prima possibile.

Il modo corretto di gestire un interrupt "impossibile" è come questo:

// When nothing will interrupt your code
try {
  Thread.sleep(1000);
} catch (InterruptedException e) {
  Thread.currentThread().interrupt();
  throw new AssertionError(e);
}

Questo fa due cose; ripristina innanzitutto lo stato di interruzione del thread (come se l' InterruptedException non fosse stato gettato in primo luogo), quindi lancia un AssertionError indica che gli invarianti di base della tua applicazione sono stati violati. Se si è certi che non si interromperà mai il thread, questo codice viene eseguito in questo modo, poiché il blocco catch non dovrebbe mai essere raggiunto.

L'uso della classe Uninterruptibles di Guava aiuta a semplificare questo schema; chiamando Uninterruptibles.sleepUninterruptibly() ignora lo stato interrotto di un thread fino a quando la durata del sonno è scaduta (a quel punto viene ripristinata per le chiamate successive da ispezionare e lanciare la propria InterruptedException ). Se sai che non interromperesti mai questo codice, eviterai in modo sicuro di dover avvolgere le tue chiamate a riposo in un blocco try-catch.

Più spesso, tuttavia, non è possibile garantire che il thread non verrà mai interrotto. In particolare se stai scrivendo un codice che verrà eseguito da un Executor o da qualche altra gestione dei thread, è fondamentale che il tuo codice risponda prontamente agli interrupt, altrimenti l'applicazione si fermerà o si bloccherà.

In questi casi, la cosa migliore da fare è in genere consentire a InterruptedException di propagare lo stack delle chiamate, aggiungendo una throws InterruptedException di throws InterruptedException a ciascun metodo a turno. Questo può sembrare imbarazzante, ma in realtà è una proprietà desiderabile - le firme del tuo metodo ora indicano ai chiamanti che risponderanno prontamente alle interruzioni.

// Let the caller determine how to handle the interrupt if you're unsure
public void myLongRunningMethod() throws InterruptedException {
  ...
}

In casi limitati (ad es. Durante l'override di un metodo che non throw eccezioni controllate) è possibile ripristinare lo stato interrotto senza generare un'eccezione, aspettandosi che qualsiasi codice venga eseguito accanto a gestire l'interrupt. Questo ritarda la gestione dell'interruzione ma non la sopprime completamente.

// Suppresses the exception but resets the interrupted state letting later code
// detect the interrupt and handle it properly.
try {
  Thread.sleep(1000);
} catch (InterruptedException e) {
  Thread.currentThread().interrupt();
  return ...; // your expectations are still broken at this point - try not to do more work.
}

La gerarchia delle eccezioni Java - Eccezioni non selezionate e controllate

Tutte le eccezioni Java sono istanze di classi nella gerarchia di classi Exception. Questo può essere rappresentato come segue:

  • java.lang.Throwable - Questa è la classe base per tutte le classi di eccezioni. I suoi metodi e costruttori implementano una gamma di funzionalità comuni a tutte le eccezioni.
    • java.lang.Exception - Questa è la superclasse di tutte le normali eccezioni.
      • varie classi di eccezioni standard e personalizzate.
      • java.lang.RuntimeException - Questa è la superclasse di tutte le eccezioni normali che sono eccezioni non controllate .
        • varie classi di eccezioni di runtime standard e personalizzate.
    • java.lang.Error - Questa è la superclasse di tutte le eccezioni di "errore fatale".

Gli appunti:

  1. La distinzione tra le eccezioni controllate e non controllate è descritta di seguito.
  2. La Throwable , Exception e RuntimeException deve essere considerata abstract ; vedi Pitfall - Throwable Throwable, Exception, Error o RuntimeException .
  3. Le eccezioni di Error vengono generate dalla JVM in situazioni in cui non sarebbe sicuro o imprudente per un'applicazione tentare di ripristinare.
  4. Non sarebbe saggio dichiarare sottotipi personalizzati di Throwable . Gli strumenti e le librerie Java possono assumere che Error ed Exception sono gli unici sottotipi diretti di Throwable e si comportano Throwable se tale presupposto non è corretto.

Controllato contro Eccezioni non selezionate

Una delle critiche al supporto delle eccezioni in alcuni linguaggi di programmazione è che è difficile sapere quali eccezioni potrebbero generare un determinato metodo o procedura. Dato che un'eccezione non gestita può causare l'arresto anomalo di un programma, ciò può rendere le eccezioni una fonte di fragilità.

Il linguaggio Java affronta questo problema con il meccanismo di eccezione verificato. Innanzitutto, Java classifica le eccezioni in due categorie:

  • Le eccezioni controllate rappresentano in genere gli eventi previsti che un'applicazione dovrebbe essere in grado di gestire. Ad esempio, IOException e i suoi sottotipi rappresentano condizioni di errore che possono verificarsi nelle operazioni di I / O. Gli esempi includono, l'apertura di file non avviene perché un file o una directory non esiste, le letture e le scritture di rete non funzionano perché una connessione di rete è stata interrotta e così via.

  • Le eccezioni non controllate tipicamente rappresentano eventi imprevisti che un'applicazione non è in grado di gestire. Questi sono in genere il risultato di un bug nell'applicazione.

(Di seguito, "gettato" si riferisce a qualsiasi eccezione lanciata esplicitamente (da un'istruzione throw ), o implicitamente (in un dereferenziamento fallito, digitare cast e così via). Allo stesso modo, "propagated" si riferisce a un'eccezione che è stata lanciata in un chiamata nidificata e non catturata all'interno di quella chiamata. Il seguente codice di esempio illustrerà questo.)

La seconda parte del meccanismo di eccezione verificata è che esistono restrizioni sui metodi in cui può verificarsi un'eccezione verificata:

  • Quando un'eccezione controllata viene lanciata o propagata in un metodo, deve essere rilevata dal metodo o elencata nella clausola di throws del metodo. (Il significato della clausola throws è descritto in questo esempio ).
  • Quando un'eccezione controllata viene lanciata o propagata in un blocco di inizializzazione, deve essere catturata dal blocco.
  • Un'eccezione verificata non può essere propagata da una chiamata di metodo in un'espressione di inizializzazione del campo. (Non c'è modo di cogliere tale eccezione).

In breve, un'eccezione controllata deve essere gestita o dichiarata.

Queste restrizioni non si applicano alle eccezioni non controllate. Ciò include tutti i casi in cui un'eccezione viene lanciata implicitamente, poiché tutti questi casi generano eccezioni non controllate.

Esempi di eccezioni controllati

Questi snippet di codice hanno lo scopo di illustrare le restrizioni delle eccezioni controllate. In ogni caso, mostriamo una versione del codice con un errore di compilazione e una seconda versione con l'errore corretto.

// This declares a custom checked exception.
public class MyException extends Exception {
    // constructors omitted.
}

// This declares a custom unchecked exception.
public class MyException2 extends RuntimeException {
    // constructors omitted.
}

Il primo esempio mostra come le eccezioni verificate esplicitamente generate possono essere dichiarate come "lanciate" se non devono essere gestite nel metodo.

// INCORRECT
public void methodThrowingCheckedException(boolean flag) {
    int i = 1 / 0;                // Compiles OK, throws ArithmeticException
    if (flag) {
        throw new MyException();  // Compilation error
    } else {
        throw new MyException2(); // Compiles OK
    }
}

// CORRECTED
public void methodThrowingCheckedException(boolean flag) throws MyException {
    int i = 1 / 0;                // Compiles OK, throws ArithmeticException
    if (flag) {
        throw new MyException();  // Compilation error
    } else {
        throw new MyException2(); // Compiles OK
    }
}

Il secondo esempio mostra come può essere gestita un'eccezione verificata propagata.

// INCORRECT 
public void methodWithPropagatedCheckedException() {
    InputStream is = new FileInputStream("someFile.txt");  // Compilation error
    // FileInputStream throws IOException or a subclass if the file cannot 
    // be opened.  IOException is a checked exception.
    ...
}

// CORRECTED (Version A) 
public void methodWithPropagatedCheckedException() throws IOException {
    InputStream is = new FileInputStream("someFile.txt"); 
    ...
}

// CORRECTED (Version B) 
public void methodWithPropagatedCheckedException() {
    try {
        InputStream is = new FileInputStream("someFile.txt"); 
        ...
    } catch (IOException ex) {
        System.out.println("Cannot open file: " + ex.getMessage());
    }
}

L'esempio finale mostra come gestire un'eccezione controllata in un inizializzatore di campo statico.

// INCORRECT
public class Test {
    private static final InputStream is = 
            new FileInputStream("someFile.txt");  // Compilation error
}

// CORRECTED
public class Test {
    private static final InputStream is;
    static {
        InputStream tmp = null;
        try {
            tmp = new FileInputStream("someFile.txt");
        } catch (IOException ex) {
            System.out.println("Cannot open file: " + ex.getMessage());
        }
        is = tmp;
    }
}

Si noti che in questo ultimo caso, abbiamo anche a che fare con i problemi che is non possono essere assegnati a più di una volta, ma anche deve essere assegnato a, anche nel caso di un'eccezione.

introduzione

Le eccezioni sono errori che si verificano quando un programma è in esecuzione. Considera il programma Java sotto il quale si dividono due numeri interi.

class Division {
    public static void main(String[] args) {
 
        int a, b, result;
 
        Scanner input = new Scanner(System.in);
        System.out.println("Input two integers");
 
        a = input.nextInt();
        b = input.nextInt();
 
        result = a / b;
 
        System.out.println("Result = " + result);
    }
}

Ora compiliamo ed eseguiamo il codice precedente e vediamo l'output per una divisione tentata per zero:

Input two integers
7 0
Exception in thread "main" java.lang.ArithmeticException: / by zero 
    at Division.main(Disivion.java:14)

La divisione per zero è un'operazione non valida che produce un valore che non può essere rappresentato come un numero intero. Java si occupa di questo lanciando un'eccezione . In questo caso, l'eccezione è un'istanza della classe ArithmeticException .

Nota: l'esempio sulla creazione e la lettura delle tracce dello stack spiega cosa significa l'output dopo i due numeri.

L'utilità di un'eccezione è il controllo del flusso che consente. Senza usare eccezioni, una soluzione tipica a questo problema potrebbe essere quella di verificare prima se b == 0 :

class Division {
    public static void main(String[] args) {
 
        int a, b, result;

        Scanner input = new Scanner(System.in);
        System.out.println("Input two integers");
 
        a = input.nextInt();
        b = input.nextInt();
 
        if (b == 0) {
            System.out.println("You cannot divide by zero.");
            return;
        }

        result = a / b;
 
        System.out.println("Result = " + result);
    }
}

Questo stampa il messaggio You cannot divide by zero. alla console e chiude il programma in modo aggraziato quando l'utente tenta di dividere per zero. Un modo equivalente di affrontare questo problema tramite la gestione delle eccezioni sarebbe quello di sostituire il controllo del flusso if con un blocco try-catch :

...

a = input.nextInt();
b = input.nextInt();
 
try {
    result = a / b;
}
catch (ArithmeticException e) {
    System.out.println("An ArithmeticException occurred. Perhaps you tried to divide by zero.");
    return;
}
 
...  

Un blocco catch try viene eseguito come segue:

  1. Inizia l'esecuzione del codice nel blocco try .
  2. Se si verifica un'eccezione nel blocco try, interrompere immediatamente e verificare se questa eccezione è catturata dal blocco catch (in questo caso, quando Exception è un'istanza di ArithmeticException ).
  3. Se l'eccezione viene catturata , viene assegnata alla variabile e e il blocco catch viene eseguito.
  4. Se il blocco try o catch è completato (ovvero non si verificano eccezioni non rilevate durante l'esecuzione del codice), continuare ad eseguire il codice sotto il blocco try-catch .

In genere, è consigliabile utilizzare la gestione delle eccezioni come parte del normale controllo del flusso di un'applicazione in cui il comportamento sarebbe altrimenti indefinito o inatteso. Ad esempio, invece di restituire null quando un metodo fallisce, solitamente è meglio lanciare un'eccezione in modo che l'applicazione che fa uso del metodo possa definire il proprio controllo di flusso per la situazione tramite la gestione delle eccezioni del tipo illustrato sopra. In un certo senso, questo aggira il problema di dover restituire un particolare tipo , poiché uno qualsiasi dei molteplici tipi di eccezioni può essere lanciato per indicare il problema specifico che si è verificato.

Per ulteriori consigli su come e in che modo non usare le eccezioni, fare riferimento a Insidie ​​di Java - Utilizzo delle eccezioni

Restituisci le dichiarazioni in try catch block

Sebbene sia una cattiva pratica, è possibile aggiungere più istruzioni di ritorno in un blocco di gestione delle eccezioni:

 public static int returnTest(int number){
    try{
        if(number%2 == 0) throw new Exception("Exception thrown");
        else return x;
    }
    catch(Exception e){
        return 3;
    }
    finally{
        return 7;
    }
}

Questo metodo restituirà sempre 7 poiché il blocco finally associato al blocco try / catch viene eseguito prima che venga restituito qualcosa. Ora, come finalmente ha return 7; , questo valore sostituisce i valori di risposta try / catch.

Se il blocco catch restituisce un valore primitivo e tale valore primitivo viene successivamente modificato nel blocco finally, verrà restituito il valore restituito nel blocco catch e le modifiche dal blocco finally verranno ignorate.

L'esempio seguente stamperà "0", non "1".

public class FinallyExample {

    public static void main(String[] args) {
        int n = returnTest(4);
        
        System.out.println(n);
    }

    public static int returnTest(int number) {
        
        int returnNumber = 0; 
        
        try {
            if (number % 2 == 0)
                throw new Exception("Exception thrown");
            else
                return returnNumber;
        } catch (Exception e) {
            return returnNumber;
        } finally {
            returnNumber = 1;
        }
    }
}

Funzionalità avanzate di Eccezioni

Questo esempio illustra alcune funzioni avanzate e casi d'uso per Eccezioni.

Esaminando programmaticamente il callstack

Java SE 1.4

L'uso principale degli stacktraces delle eccezioni consiste nel fornire informazioni su un errore dell'applicazione e il relativo contesto in modo che il programmatore possa diagnosticare e risolvere il problema. A volte può essere usato per altre cose. Ad esempio, una classe SecurityManager potrebbe dover esaminare lo stack di chiamate per decidere se il codice che sta effettuando una chiamata deve essere considerato attendibile.

È possibile utilizzare le eccezioni per esaminare lo stack di chiamate in modo programmatico come segue:

    Exception ex = new Exception();   // this captures the call stack
    StackTraceElement[] frames = ex.getStackTrace();
    System.out.println("This method is " + frames[0].getMethodName());
    System.out.println("Called from method " + frames[1].getMethodName());

Ci sono alcuni avvertimenti importanti su questo:

  1. Le informazioni disponibili in StackTraceElement sono limitate. Non ci sono più informazioni disponibili di quelle visualizzate da printStackTrace . (I valori delle variabili locali nel frame non sono disponibili.)

  2. I javadoc per getStackTrace() indicano che una JVM può lasciare i frame:

    Alcune macchine virtuali possono, in alcune circostanze, omettere uno o più frame dello stack dall'analisi dello stack. Nel caso estremo, una macchina virtuale che non ha informazioni sulla traccia di stack relative a questo gettabile è autorizzata a restituire una matrice a lunghezza zero da questo metodo.

Ottimizzazione della costruzione di eccezioni

Come accennato altrove, la costruzione di un'eccezione è piuttosto costosa in quanto comporta l'acquisizione e la registrazione di informazioni su tutti i frame di stack sul thread corrente. A volte, sappiamo che quell'informazione non verrà mai usata per una determinata eccezione; ad esempio, lo stacktrace non verrà mai stampato. In tal caso, esiste un trucco di implementazione che è possibile utilizzare in un'eccezione personalizzata per impedire che le informazioni vengano acquisite.

Le informazioni sul frame dello stack necessarie per gli stacktraces, vengono acquisite quando i costruttori Throwable chiamano il metodo Throwable.fillInStackTrace() . Questo metodo è public , il che significa che una sottoclasse può sovrascriverla. Il trucco è scavalcare il metodo ereditato da Throwable con uno che non fa nulla; per esempio

  public class MyException extends Exception {
      // constructors

      @Override 
      public void fillInStackTrace() {
          // do nothing
      }
  }

Il problema con questo approccio è che un'eccezione che sovrascrive fillInStackTrace() non può mai acquisire lo stacktrace ed è inutile negli scenari in cui ne hai bisogno.

Cancellazione o sostituzione dello stacktrace

Java SE 1.4

In alcune situazioni, lo stacktrace per un'eccezione creata nel modo normale contiene informazioni errate o informazioni che lo sviluppatore non desidera rivelare all'utente. Per questi scenari, è possibile utilizzare Throwable.setStackTrace per sostituire la matrice di oggetti StackTraceElement che contiene le informazioni.

Ad esempio, è possibile utilizzare quanto segue per eliminare le informazioni sullo stack di un'eccezione:

 exception.setStackTrace(new StackTraceElement[0]);

Eccezioni soppresse

Java SE 7

Java 7 ha introdotto il costrutto try-with-resources e il concetto associato di soppressione delle eccezioni. Considera il seguente frammento:

try (Writer w = new BufferedWriter(new FileWriter(someFilename))) {
    // do stuff
    int temp = 0 / 0;    // throws an ArithmeticException
}

Quando viene lanciata l'eccezione, la try chiamerà close() su w che svuoterà qualsiasi output bufferizzato e quindi chiuderà FileWriter . Ma cosa succede se viene generata una IOException mentre si scarica l'output?

Quello che succede è che ogni eccezione che viene lanciata mentre si ripulisce una risorsa viene soppressa . L'eccezione viene rilevata e aggiunta all'elenco delle eccezioni soppresse dell'eccezione primaria. Quindi il try-with-resources continuerà con la pulizia delle altre risorse. Infine, l'eccezione primaria verrà riconsiderata.

Un modello simile si verifica se un'eccezione viene generata durante l'inizializzazione della risorsa o se il blocco try completato normalmente. La prima eccezione generata diventa l'eccezione principale e quelli successivi derivanti dalla pulizia vengono eliminati.

Le eccezioni soppresse possono essere recuperate dall'oggetto eccezione principale chiamando getSuppressedExceptions .

Le dichiarazioni try-finally e try-catch-finally

L'istruzione try...catch...finally combina la gestione delle eccezioni con il codice clean-up. Il blocco finally contiene il codice che verrà eseguito in tutte le circostanze. Questo li rende adatti per la gestione delle risorse e altri tipi di pulizia.

Prova-finalmente

Ecco un esempio della forma più semplice ( try...finally ):

try {
    doSomething();  
} finally {
    cleanUp();
}

Il comportamento del try...finally è il seguente:

  • Il codice nel blocco try viene eseguito.
  • Se non è stata lanciata alcuna eccezione nel blocco try :
    • Il codice nel blocco finally viene eseguito.
    • Se il blocco finally genera un'eccezione, quell'eccezione viene propagata.
    • Altrimenti, il controllo passa alla successiva dichiarazione dopo il try...finally .
  • Se è stata generata un'eccezione nel blocco try:
    • Il codice nel blocco finally viene eseguito.
    • Se il blocco finally genera un'eccezione, quell'eccezione viene propagata.
    • Altrimenti, l'eccezione originale continua a propagarsi.

Il codice all'interno del blocco finally verrà sempre eseguito. (Le uniche eccezioni sono se viene chiamato System.exit(int) o se i panni della JVM.) Quindi un blocco finally è il codice posto corretto che deve sempre essere eseguito; ad esempio chiudere file e altre risorse o rilasciare blocchi.

try-catch-finally

Il nostro secondo esempio mostra come catch e, finally può essere utilizzato insieme. Illustra anche che pulire le risorse non è semplice.

// This code snippet writes the first line of a file to a string
String result = null;
Reader reader = null;
try {
    reader = new BufferedReader(new FileReader(fileName));
    result = reader.readLine();
} catch (IOException ex) {
    Logger.getLogger.warn("Unexpected IO error", ex);  // logging the exception
} finally {
    if (reader != null) {
        try {
            reader.close();
        } catch (IOException ex) {
            // ignore / discard this exception
        }
    }
}

L'insieme completo di (ipotetici) comportamenti di try...catch...finally in questo esempio sono troppo complicati per descrivere qui. La versione semplice è che il codice nel blocco finally verrà sempre eseguito.

Guardando questo dal punto di vista della gestione delle risorse:

  • Dichiariamo la "risorsa" (cioè la variabile del reader ) prima del blocco try modo che sia in scope per il blocco finally .
  • Inserendo il new FileReader(...) , il catch è in grado di gestire qualsiasi eccezione IOError generata quando si apre il file.
  • Abbiamo bisogno di un reader.close() nel blocco finally perché ci sono alcuni percorsi di eccezione che non possiamo intercettare né nel blocco try né nel blocco catch .
  • Tuttavia, poiché un'eccezione potrebbe essere stata lanciata prima che il reader fosse inizializzato, abbiamo anche bisogno di un test null esplicito.
  • Infine, la chiamata a reader.close() potrebbe (ipoteticamente) generare un'eccezione. Non ci interessa, ma se non rileviamo l'eccezione alla fonte, avremmo bisogno di occuparci ulteriormente dello stack delle chiamate.
Java SE 7

Java 7 e versioni successive forniscono una sintassi alternativa try-with-resources che semplifica notevolmente la pulizia delle risorse.

La clausola 'getta' in una dichiarazione di metodo

Il meccanismo delle eccezioni controllate di Java richiede che il programmatore dichiari che determinati metodi potrebbero generare eccezioni controllate specificate. Questo viene fatto usando la clausola throws . Per esempio:

public class OddNumberException extends Exception { // a checked exception
}

public void checkEven(int number) throws OddNumberException {
    if (number % 2 != 0) {
        throw new OddNumberException();
    }
}

Il throws OddNumberException dichiara che una chiamata a checkEven potrebbe generare un'eccezione di tipo OddNumberException .

Una clausola throws può dichiarare un elenco di tipi e può includere eccezioni non verificate e eccezioni controllate.

public void checkEven(Double number) 
        throws OddNumberException, ArithmeticException {
    if (!Double.isFinite(number)) {
        throw new ArithmeticException("INF or NaN");
    } else if (number % 2 != 0) {
        throw new OddNumberException();
    }
}

Qual è il punto di dichiarare le eccezioni non controllate come generate?

La clausola throws in una dichiarazione di metodo ha due scopi:

  1. Indica al compilatore quali eccezioni vengono lanciate in modo che il compilatore possa segnalare eccezioni non verificate (controllate) come errori.

  2. Indica a un programmatore che sta scrivendo il codice che chiama il metodo quali eccezioni aspettarsi. A tale scopo, fa spesso intendere di includere eccezioni non selezionate in una lista di throws .

Nota: che la lista dei throws viene anche utilizzata dallo strumento javadoc durante la generazione della documentazione API e da un tipico suggerimento del metodo "testo hover" dell'IDE.

Tiri e metodo di esclusione

La clausola throws fa parte della firma di un metodo allo scopo di sovrascrivere il metodo. Un metodo di override può essere dichiarato con lo stesso insieme di eccezioni verificate come generate dal metodo sottoposto a override o con un sottoinsieme. Tuttavia, il metodo di sovrascrittura non può aggiungere eccezioni controllate supplementari. Per esempio:

@Override
public void checkEven(int number) throws NullPointerException // OK—NullPointerException is an unchecked exception
    ...

@Override
public void checkEven(Double number) throws OddNumberException // OK—identical to the superclass
    ...

class PrimeNumberException extends OddNumberException {}
class NonEvenNumberException extends OddNumberException {}

@Override
public void checkEven(int number) throws PrimeNumberException, NonEvenNumberException // OK—these are both subclasses

@Override
public void checkEven(Double number) throws IOExcepion         // ERROR

Il motivo di questa regola è che se un metodo sovrascritto può generare un'eccezione verificata che il metodo sottoposto a override non è in grado di generare, ciò interromperà la sostituibilità del tipo.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow