Em Java, qual é a diferença entre capturar uma exceção genérica e uma exceção específica (por exemplo, IOException?)

Atualmente eu estou pegando apenas exceções genéricas, mas eu quero mudar isso para pegar as exceções específicas, mas qual é a vantagem disso?

A diferença entre executar uma instrução try / catch geral e capturar uma exceção específica (por exemplo, uma FileNotFoundException) normalmente depende de quais erros você precisa tratar e com quais erros não precisa se preocupar. Por exemplo:

catch (Exception e) { //A (too) general exception handler ... } 

O código acima irá capturar TODAS as exceções que são lançadas dentro da instrução try. Mas talvez você não queira lidar com todos os erros. O que você pode fazer com uma exceção “OutOfMemory”?

Um método melhor de tratamento de erros seria realizar alguma ação padrão se o erro é desconhecido ou algo que você não pode fazer nada, e realizar outra ação se descobrir que você pode fazer “Plano B” se você pegar.

Por exemplo, suponha que você esteja tentando abrir um arquivo, mas o arquivo não existe. Você pode pegar o FileNotFoundException e criar um novo arquivo em branco como abaixo:

 catch (FileNotFoundException e) { //A specific exception handler //create a new file and proceed, instead of throwing the error to the user }catch (Exception e) { //For all other errors, alert the user ... } 

Este foi o método mais eficaz e amigável de verificação de erros que usei no passado.

Capturar exceções específicas permite que você personalize respostas específicas para cada caso.

Em um nível lógico, uma série de blocos catch é o mesmo que ter um bloco catch e, em seguida, gravar sua própria lógica condicional dentro do bloco catch único. Observe que a lógica condicional também teria que converter a exceção como subtipos específicos se você quiser acessar informações detalhadas declaradas no subtipo.

As poucas desvantagens de capturar cada exceção separadamente incluem toda a estrutura try-catch crescendo muito e fazendo com que a lógica do método de conter seja mais difícil e tendo que repetir o código em muitos ou todos os blocos catch separados (por exemplo, registrando a exceção ).

Em certos casos, a complexidade de alguma API subjacente garante o tratamento de todas as exceções diferentes e a extração da estrutura try-catch em um método utilitário. Por exemplo, a invocação de método por meio da reflection parece justificar regularmente ter APIs de fachada.

Em um nível de design de API, há sempre um ato de equilíbrio entre

  • Uma hierarquia de exceções (pública) muito rica
  • Incorporando códigos de erro como parte das informações contidas em alguma exceção de base, e
  • Um conjunto público de interfaces de marcador e usando subtipos de exceção particulares

Um bom exemplo que mostra a capacidade de lidar com problemas com base no tipo de problema que ocorreu:

 try { // open a file based on its file name } catch (FileNotFoundException e) { // indicate that the user specified a file that doesn't exist. // reopen file selection dialog box. } catch (IOException e) { // indicate that the file cannot be opened. } 

enquanto o correspondente:

 try { // open a file based on its file name. } catch (Exception e) { // indicate that something was wrong // display the exception's "reason" string. } 

O último exemplo não fornece meios para manipular a exceção com base no problema que ocorreu. Todos os problemas são tratados da mesma maneira.

Se você tiver um bloco de código que pode lançar exceções diferentes e cercar isso com uma tentativa geral {} catch {Exception e}, você não saberá exatamente o que aconteceu e como deve lidar com o erro.

Se você planeja usar várias pessoas usando seu aplicativo, ter exceções específicas permitirá que você saiba exatamente onde seu programa falhou quando estiver sob o controle de outra pessoa. mas, além disso, se o programa for apenas para você mesmo, você pode simplesmente executá-lo por meio de um depurador, embora tenha o hábito de tornar o tratamento de erros muito descritivo e não ambíguo uma excelente opção, caso planeje programação para as massas 🙂

Veja este exemplo:

 try { StringBuffer fileData = new StringBuffer(1000); BufferedReader reader = new BufferedReader( new FileReader(filePath)); char[] buf = new char[1024]; int numRead=0; while((numRead=reader.read(buf)) != -1){ fileData.append(buf, 0, numRead); } reader.close(); return fileData.toString(); } catch (Exception e) { //do something generic - maybe log it } 

Tal como está, funciona … normalmente. No entanto, com o erro vago de pegar eu realmente não posso fazer nada, exceto avisar o usuário. Se eu pegasse o FileNotFoundException especificamente, poderia tentar outro arquivo. Se eu pegasse o IOException especificamente, poderia avisar sobre outra coisa. Este exemplo é um pouco fraco, mas pode dar uma ideia.

O problema com a captura de exceções genéricas é que você acaba pegando (e muitas vezes manipulando incorretamente) uma exceção inesperada. Por exemplo:

  public String readFile(File file) { try { Reader r = new FileReader(file); // read file return ...; // the file contents } catch (Exception ex) { // file not found ... return ""; } } 

Como você pode ver, o texto acima é escrito com base na suposição de que a única maneira de o código dentro da try falhar é se o arquivo estiver faltando ou não puder ser aberto por algum motivo. De fato, se o método é chamado com um arquivo null , ou se houver algum bug no código que lê o arquivo, NPEs e outras exceções não verificadas são possíveis. Assim, o código vai esconder os erros capturando Exception .

A versão correta do código acima pegaria IOException (ou talvez FileNotFoundException ) e deixaria as exceções inesperadas se propagarem.